Spring 中的多数据源配置

Spring的多数据源配置中会用到AbstractRoutingDataSource,从名字也可以看出他的作用,抽象的带路由的数据源,源码的说明如下:

* Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
 * calls to one of various target DataSources based on a lookup key. The latter is usually
 * (but not necessarily) determined through some thread-bound transaction context.

类的声明如下:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
......
}

那他是如何做到多数据源访问的呢,跟下代码,AbstractRoutingDataSource的getConnection方法:

@Override
    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }
determineTargetDataSource方法:
/**
     * Retrieve the current target DataSource. Determines the
     * {@link #determineCurrentLookupKey() current lookup key}, performs
     * a lookup in the {@link #setTargetDataSources targetDataSources} map,
     * falls back to the specified
     * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
     * @see #determineCurrentLookupKey()
     */
    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();//获取key
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);//根据key获取真正的数据源,当然前提是resolvedDataSources有值,这也是子类实现中做的事情
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }
determineCurrentLookupKey方法:
    /**
     * Determine the current lookup key. This will typically be
     * implemented to check a thread-bound transaction context.
     * <p>Allows for arbitrary keys. The returned key needs
     * to match the stored lookup key type, as resolved by the
     * {@link #resolveSpecifiedLookupKey} method.
     */
    @Nullable
    protected abstract Object determineCurrentLookupKey();

注意这是一个抽象方法,是留给子类实现具体的选择数据源的方法,从方法声明中知道返回的是一个的lookupkey,即数据源的key。

那子类应该如何写呢,以若依框架中的多数据源配置为例:

package com.ruoyi.framework.datasource;

import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源:继承在AbstractRoutingDataSource,并设置了父类的targetDataSources,决定使用数据源的key
 * @author ruoyi
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
     //设置父类的targetDataSources,供获取connection时选择使用哪一个数据源
super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override
    //决定使用的数据源的key
protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
package com.ruoyi.framework.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据源切换处理
 * 
 * @author ruoyi
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

此类借助于ThreadLocal用于设置和获取具体的数据源的key。

原文地址:https://www.cnblogs.com/silenceshining/p/14284800.html