• 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>();
    </span><span style="color: #008000;">/**</span><span style="color: #008000;"></br>
     * 数据库源类型</br>
     * &lt;p&gt;</br>
     * 配置数据源的时候,请遵守以下约束:&lt;br&gt;</br>
     * 读写:dataSourceKeyRW;&lt;br&gt;</br>
     * 读:dataSourceKeyR.</br>
     * &lt;/p&gt;</br>
     </span><span style="color: #008000;">*/</span></br>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">enum</span><span style="color: #000000;"> DbType {</br>
        DB_TYPE_RW(</span>"dataSourceKeyRW"), DB_TYPE_R("dataSourceKeyR"<span style="color: #000000;">);</br>
        </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> String dataSourceKey;</br>
    
        DbType(String dataSourceKey) {</br>
            </span><span style="color: #0000ff;">this</span>.dataSourceKey =<span style="color: #000000;"> dataSourceKey;</br>
        }</br>
    
        </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getDataSourceKey() {</br>
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> dataSourceKey;</br>
        }</br>
    }
    
    </span><span style="color: #008000;">/**</span><span style="color: #008000;"></br>
     * 获取数据源</br>
     * &lt;p&gt;</br>
     * 如果未设置,默认返回读数据源</br>
     * &lt;/p&gt;</br>
     *</br>
     * </span><span style="color: #808080;">@return</span><span style="color: #008000;"> 数据源键值</br>
     </span><span style="color: #008000;">*/</span></br>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> String getDataSource() {</br>
        String dataSource </span>=<span style="color: #000000;"> contextHolder.get();</br>
        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (StringUtils.isEmpty(dataSource)) {</br>
            dataSource </span>=<span style="color: #000000;"> DbType.DB_TYPE_RW.dataSourceKey;</br>
        }</br>
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> dataSource;</br>
    }</br>
    
    </span><span style="color: #008000;">/**</span><span style="color: #008000;">
     * 设置数据源</br>
     *
     * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> dataSourceKey 数据源键值</br>
     </span><span style="color: #008000;">*/</span></br>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setDataSource(String dataSourceKey) {</br>
        contextHolder.set(dataSourceKey);</br>
    }</br>
    

    }

    复制代码

    注:定义了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());
    }
    </span><span style="color: #008000;">/**</span><span style="color: #008000;"></br>
     * 切换数据源</br>
     * &lt;p&gt;</br>
     * 切换优先级由高到底如下;方法上注解DataSourceKey,方法上注解ReadOnlyKey,类上注解DataSourceKey;&lt;br&gt;</br>
     * 如果未注解,则默认设置写数据源.</br>
     * &lt;/p&gt;</br>
     *
     * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> joinPoint 切点</br>
     * </span><span style="color: #808080;">@see</span><span style="color: #008000;"> DataSourceKey</br>
     * </span><span style="color: #808080;">@see</span><span style="color: #008000;"> ReadOnlyKey</br>
     * </span><span style="color: #808080;">@see</span><span style="color: #008000;"> DbType</br>
     </span><span style="color: #008000;">*/</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> switchDataSource(JoinPoint joinPoint) {</br>
        Class</span>&lt;?&gt; targetClass =<span style="color: #000000;"> joinPoint.getTarget().getClass();</br>
        String methodName </span>=<span style="color: #000000;"> joinPoint.getSignature().getName();</br>
        Object[] args </span>=<span style="color: #000000;"> joinPoint.getArgs();</br>
        DataSourceKey dataSourceKey </span>= getAnnotationClassMethod(targetClass, methodName, DataSourceKey.<span style="color: #0000ff;">class</span><span style="color: #000000;">, args);</br>
        </span><span style="color: #0000ff;">if</span> (dataSourceKey != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</br>
            setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());</br>
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;</br>
        }</br>
        ReadOnlyKey readOnlyKey </span>= getAnnotationClassMethod(targetClass, methodName, ReadOnlyKey.<span style="color: #0000ff;">class</span><span style="color: #000000;">, args);</br>
        </span><span style="color: #0000ff;">if</span> (readOnlyKey != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</br>
            setDataSourceByKey(joinPoint, DbType.DB_TYPE_R.getDataSourceKey());</br>
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;</br>
        }</br>
        dataSourceKey </span>= (DataSourceKey) targetClass.getAnnotation(DataSourceKey.<span style="color: #0000ff;">class</span><span style="color: #000000;">);</br>
        </span><span style="color: #0000ff;">if</span> (dataSourceKey != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</br>
            setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());</br>
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;</br>
        }</br>
        setDataSourceByKey(joinPoint, DbType.DB_TYPE_RW.getDataSourceKey());</br>
    }</br>
    

    }

    复制代码

    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。

  • 相关阅读:
    Java学习过程中的总结的小知识点(长期更新)
    年月日与time的相互转换
    Androidstudio预览时出现错误java.lang.NoClassDefFoundError: com/android/util/PropertiesMap
    eclipse中配置struts2出现There is no Action mapped for namespace [/] and action name [Login] associated wi
    struts2出错java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils
    第一次部署Struts2时出现错误java.lang.ClassNotFoundException: org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.class
    由JDK1.8降为JDK1.6时配置环境变量不起作用
    Androidstudio报错UnsupportedClassVersionError
    AndroidStudio导入Library
    Ubuntu下su被拒绝
  • 原文地址:https://www.cnblogs.com/jpfss/p/8298775.html
Copyright © 2020-2023  润新知