Skip to content

原理分析

liuqingyao edited this page Mar 22, 2017 · 9 revisions

1.自定义DataSource

Spring JDBC的datasource中有个抽象类:AbstractRoutingDataSource(抽象类)具备动态切换DataSource(接口)的特性 AbstractRoutingDataSource extends AbstractDataSource implements DataSource

AbstractRoutingDataSource的属性

    private Map<Object, Object> targetDataSources;
    private Object defaultTargetDataSource;
    private boolean lenientFallback = true;
    private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
    private Map<Object, DataSource> resolvedDataSources;//这个属性很重要,说明具备多数据源能力
    private DataSource resolvedDefaultDataSource;

AbstractRoutingDataSource的关键方法

protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        //下两行代码实现了数据源的切换,而用哪个数据源最终由determineCurrentLookupKey决定(返回了一个key)
        Object lookupKey = this.determineCurrentLookupKey();
        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
        if(dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }

        if(dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        } else {
            return dataSource;
        }
}
//留给我们做扩展用的
protected abstract Object determineCurrentLookupKey();

MultiDataSource的实现细节

package com.company.component.datasource.multi_datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.HashMap;
import java.util.Map;

/**
 * User: yaoliuqing
 * Time: 14-9-26 下午4:46
 */
public class MultipleDataSource extends AbstractRoutingDataSource {

    //保证线程安全
    private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<String>();

    private static final Map<String, String> packageDataSource = new HashMap<String, String>();

    public static void setDataSourceKey(String dataSource) {
        dataSourceKey.set(dataSource);
    }

    public static void usePackageDataSource(String pkgName) {
        dataSourceKey.set(packageDataSource.get(pkgName));
    }

    //实现AbstractRoutingDataSource的determineTargetDataSource
    protected Object determineCurrentLookupKey() {
        String dsName = dataSourceKey.get();
        //阅后即焚
        dataSourceKey.remove();
        return dsName;
    }

    public Map<String, String> getPackageDataSource() {
        return MultipleDataSource.packageDataSource;
    }

    public void setPackageDataSource(Map<String, String> packageDataSource) {
        MultipleDataSource.packageDataSource.putAll(packageDataSource);
    }

}

将原来我们在配置中使用的数据源替换成我们自定义的数据源

2.定义注解

有了自定义数据源后,我们也要更方便地指定编码中具体某个方法使用哪个数据源(其实不自定义数据源也是可以实现多数据源在同一个项目中使用的,非常麻烦和不灵活以至于我已经记不得怎么用了^_^) 如果通过注解就可以指定某个方法用哪个数据源,岂不方便?!特别是如果你工程正处于数据库拆分,数据库不停加从库的场景下。 于是定义了一个注解

package com.company.component.datasource.multi_datasource.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * User: yaoliuqing
 * Time: 14-9-25 下午5:27
 * 指定操作的数据库
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD})
public @interface DataSource {

    /**
     * value dataSource的名称
     */
    String value();

}

3.切面中实现根据注解选择DataSource

Clone this wiki locally