程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> springAOP實現基於注解的數據源動態切換,springaop注解

springAOP實現基於注解的數據源動態切換,springaop注解

編輯:JAVA綜合教程

springAOP實現基於注解的數據源動態切換,springaop注解


需求

代碼實現讀寫數據庫分離

武器

spring3.0以上版本

實現思路

1、繼承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,自定義數據源路由。

2、實現數據源類型管理工具,諸如DBContextHolder,包含設置和讀取當前數據源配置。

3、實現數據源切換的AOP。

4、自定義只讀注解,諸如@ReadOnlyKey。

5、配置transactionManager,實現aop。

代碼示例

1、自定義的DynamicDataSource

public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 自動查找數據源
     *
     * @return 數據源名
     */
    @Override
    protected Object determineCurrentLookupKey() {
        String dataSource = getDataSource();
        return dataSource;
    }
}

2、數據源類型管理工具DBContextHolder

public abstract class DBContextHolder {
    /**
     * 數據源類型管理
     * <p>
     * 考慮多線程,為保證線程之間互不干擾,所以使用ThreadLocal作線程隔離;<br>
     * 參數是數據源鍵值
     * </p>
     *
     * @see ThreadLocal
     */
    private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    /**
     * 數據庫源類型
     * <p>
     * 配置數據源的時候,請遵守以下約束:<br>
     * 讀寫:dataSourceKeyRW;<br>
     * 讀:dataSourceKeyR.
     * </p>
     */
    public enum DbType {
        DB_TYPE_RW("dataSourceKeyRW"), DB_TYPE_R("dataSourceKeyR");
        private String dataSourceKey;

        DbType(String dataSourceKey) {
            this.dataSourceKey = dataSourceKey;
        }

        public String getDataSourceKey() {
            return dataSourceKey;
        }
    }

    /**
     * 獲取數據源
     * <p>
     * 如果未設置,默認返回讀數據源
     * </p>
     *
     * @return 數據源鍵值
     */
    public static String getDataSource() {
        String dataSource = contextHolder.get();
        if (StringUtils.isEmpty(dataSource)) {
            dataSource = DbType.DB_TYPE_RW.dataSourceKey;
        }
        return dataSource;
    }

    /**
     * 設置數據源
     *
     * @param dataSourceKey 數據源鍵值
     */
    public static void setDataSource(String dataSourceKey) {
        contextHolder.set(dataSourceKey);
    }
}

注:定義了DbType枚舉,分別定義了讀和寫的數據源鍵值。

3、實現AOP。

public class DataSourceSwitchingAop {
    /**
     * 設置切點數據源
     * <p>
     * 調試輸出數據源.
     * </p>
     *
     * @param joinPoint     切點
     * @param dataSourceKey 當前數據源鍵值
     */
    private void setDataSourceByKey(JoinPoint joinPoint, String dataSourceKey) {
        setDataSource(dataSourceKey);
        debugLog(joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName() + "配置數據源:" + getDataSource());
    }

    /**
     * 切換數據源
     * <p>
     * 切換優先級由高到底如下;方法上注解DataSourceKey,方法上注解ReadOnlyKey,類上注解DataSourceKey;<br>
     * 如果未注解,則默認設置寫數據源.
     * </p>
     *
     * @param joinPoint 切點
     * @see DataSourceKey
     * @see ReadOnlyKey
     * @see DbType
     */
    public void switchDataSource(JoinPoint joinPoint) {
        Class<?> targetClass = joinPoint.getTarget().getClass();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        DataSourceKey dataSourceKey = getAnnotationClassMethod(targetClass, methodName, DataSourceKey.class, args);
        if (dataSourceKey != null) {
            setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());
            return;
        }
        ReadOnlyKey readOnlyKey = getAnnotationClassMethod(targetClass, methodName, ReadOnlyKey.class, args);
        if (readOnlyKey != null) {
            setDataSourceByKey(joinPoint, DbType.DB_TYPE_R.getDataSourceKey());
            return;
        }
        dataSourceKey = (DataSourceKey) targetClass.getAnnotation(DataSourceKey.class);
        if (dataSourceKey != null) {
            setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());
            return;
        }
        setDataSourceByKey(joinPoint, DbType.DB_TYPE_RW.getDataSourceKey());
    }
}

4、自定義只讀注解,@ReadOnlyKey

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ReadOnlyKey {
}

5、配置transaction和AOP

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dynamicDataSource"/>
    </bean>
<bean id="dataSourceSwitchingAop" class="com.xxx.common.framework2x.dao.DataSourceSwitchingAop"/>
<aop:config>
        <aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">
            <aop:pointcut id="dataSourceSwitchingService"
                          expression="execution(* com.xxx.manager..*.*(..))"/>
            <aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>
        </aop:aspect>
    </aop:config>

以上就完成了基於注解實現動態切換讀寫數據源。

6、如果想要實現多數據源的切換,則可以自定義注解@DataSourceKey

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSourceKey {
    /**
     * 配置數據源鍵值
     * <p>
     * 默認:dataSource.
     * </p>
     *
     * @return 鍵值
     */
    String dataSourceKey() default "dataSource";
}

在接口方法上增加注解即可。

需要特別注意的地方

1、切換數據源的事務需要放到數據庫事務開啟前執行。針對上述代碼示例中,配置aop時需要指定order(值越小,執行越靠前)

<aop:config>
        <aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">
            <aop:pointcut id="dataSourceSwitchingService"
                          expression="execution(* com.xxx.manager..*.*(..))"/>
            <aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>
        </aop:aspect>
    </aop:config>

2、@DataSourceKey可以加在method上,也可以加到class上,優先級是method>class。

3、@ReadOnlyKey只能加到method上。

4、@DatasourceKey和@ReadOnlyKey可以在一個class中混用,優先級是method的@DatasourceKey>method的@ReadOnlyKey>class的@DatasourceKey。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved