天天看點

java 動态 資料源_Java注解實作動态資料源切換的執行個體代碼

當一個項目中有多個資料源(也可以是主從庫)的時候,我們可以利用注解在mapper接口上标注資料源,進而來實作多個資料源在運作時的動态切換。

實作原理

在Spring 2.0.1中引入了AbstractRoutingDataSource, 該類充當了DataSource的路由中介, 能有在運作時, 根據某種key值來動态切換到真正的DataSource上。

看下AbstractRoutingDataSource:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean

AbstractRoutingDataSource繼承了AbstractDataSource,擷取資料源部分:

protected DataSource determineTargetDataSource() {

Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");

Object lookupKey = determineCurrentLookupKey();

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 + "]");

}

return dataSource;

}

抽象方法determineCurrentLookupKey()傳回DataSource的key值,然後根據這個key從resolvedDataSources這個map裡取出對應的DataSource,如果找不到,則用預設的resolvedDefaultDataSource。

我們要做的就是實作抽象方法determineCurrentLookupKey()傳回資料源的key值。

使用方法

定義注解:

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface DataSource {

DataSourceType value();

}

注解為資料源的名稱,可定義一個枚舉類表示:

public enum DataSourceType {

MASTER,

SLAVE

}

注解定義好了,我們利用Spring的AOP根據注解内容對資料源進行選擇,這裡需要利用上面提到的 AbstractRoutingDataSource類,該類是能夠實作資料源切換的關鍵所在。

定義類DynamicDataSource繼承AbstractRoutingDataSource,并實作 determineCurrentLookupKey(),傳回資料源的key值。

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return DynamicDataSourceHolder.getDataSourceType();

}

}

DynamicDataSourceHolder是我們管理DataSource的類,将一次資料庫操作的資料源名稱儲存在DynamicDataSourceHolder中,以供後面的操作在此context中取資料源key,其中DataSourceType使用了線程本地變量來保證線程安全。

public class DynamicDataSourceHolder {

// 線程本地環境

private static final ThreadLocal contextHolder = new ThreadLocal();

// 設定資料源類型

public static void setDataSourceType(DataSourceType dataSourceType) {

Assert.notNull(dataSourceType, "DataSourceType cannot be null");

contextHolder.set(dataSourceType);

}

// 擷取資料源類型

public static DataSourceType getDataSourceType() {

return (DataSourceType) contextHolder.get();

}

// 清除資料源類型

public static void clearDataSourceType() {

contextHolder.remove();

}

}

我們在Spring的配置檔案中配置資料源key值得對應關系:

設定targetDataSources和defaultTargetDataSource。 TEST-MASTER-DB和 TEST-SLAVE-DB表示主庫的從庫,是我們的兩個資料源。

接下來配置AOP切面:

id="dataSourceCutPoint" />

以下是切面中before執行的DataSourceAspect的實作,主要實作的功能是擷取方法上的注解,根據注解名稱将值設定到DynamicDataSourceHolder中,這樣在執行查詢的時候,determineCurrentLookupKey()傳回資料源的key值就是我們希望的那個資料源了。

public class DataSourceAspect {

private static final Logger LOG = LoggerFactory.getLogger(DataSourceAspect.class);

public void before(JoinPoint point){

Object target = point.getTarget();

String method = point.getSignature().getName();

Class>[] classz = target.getClass().getInterfaces();

Class>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();

try {

Method m = classz[0].getMethod(method, parameterTypes);

if (m != null && m.isAnnotationPresent(DataSource.class)) {

// 通路mapper中的注解

DataSource data = m.getAnnotation(DataSource.class);

switch (data.value()) {

case MASTER:

DynamicDataSourceHolder.setDataSourceType(DataSourceType.MASTER);

LOG.info("using dataSource:{}", DataSourceType.MASTER);

break;

case SLAVE:

DynamicDataSourceHolder.setDataSourceType(DataSourceType.SLAVE);

LOG.info("using dataSource:{}", DataSourceType.SLAVE);

break;

}

}

} catch (Exception e) {

LOG.error("dataSource annotation error:{}", e.getMessage());

// 若出現異常,手動設為主庫

DynamicDataSourceHolder.setDataSourceType(DataSourceType.MASTER);

}

}

}

這樣我們就實作了一個動态資料源切換的功能。

以上就是本文的全部内容,希望對大家的學習有所幫助,也希望大家多多支援腳本之家。