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>
 * <p></br>
 * 配置数据源的时候,请遵守以下约束:<br></br>
 * 读写:dataSourceKeyRW;<br></br>
 * 读:dataSourceKeyR.</br>
 * </p></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>
 * <p></br>
 * 如果未设置,默认返回读数据源</br>
 * </p></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>
 * <p></br>
 * 切换优先级由高到底如下;方法上注解DataSourceKey,方法上注解ReadOnlyKey,类上注解DataSourceKey;<br></br>
 * 如果未注解,则默认设置写数据源.</br>
 * </p></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><?> 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。
SPRINGAOP实现基于注解的数据源动态切换(转)的更多相关文章
- springAOP实现基于注解的数据源动态切换
		需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ... 
- Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法
		一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ... 
- Springboot多数据源配置--数据源动态切换
		在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ... 
- springboot多数据源动态切换和自定义mybatis分页插件
		1.配置多数据源 增加druid依赖 完整pom文件 数据源配置文件 route.datasource.driver-class-name= com.mysql.jdbc.Driver route.d ... 
- Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法
		一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ... 
- Spring多数据源动态切换
		title: Spring多数据源动态切换 date: 2019-11-27 categories: Java Spring tags: 数据源 typora-root-url: ...... --- ... 
- mybatis 多数据源动态切换
		笔者主要从事c#开发,近期因为项目需要,搭建了一套spring-cloud微服务框架,集成了eureka服务注册中心. gateway网关过滤.admin服务监控.auth授权体系验证,集成了redi ... 
- 实战:Spring AOP实现多数据源动态切换
		需求背景 去年底,公司项目有一个需求中有个接口需要用到平台.算法.大数据等三个不同数据库的数据进行计算.组装以及最后的展示,当时这个需求是另一个老同事在做,我只是负责自己的部分. 直到今年回来了,这个 ... 
- 基于注解实现jackson动态JsonProperty
		基于注解实现jackson动态JsonProperty @JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,但是值是固定的 ... 
随机推荐
- CentOs虚拟机能够互相ping通,但无法訪问虚拟机服务
			CentOs虚拟机能够互相ping通,但无法訪问虚拟机服务 虚拟机ip:192.168.0.57 主机 ip:192.168.0.80 在虚拟机上搭建了php环境.虚拟机CentOs,主机win7 虚 ... 
- 转:关于ios 推送功能的终极解决
			刚刚做了一个使用推送功能的应用 遇到了一些问题整的很郁闷 搞了两天总算是弄明白了 特此分享给大家 本帖 主要是针对产品发布版本的一些问题 综合了网上一些资料根据自己实践写的 不过测试也可以看看 首先要 ... 
- opencms9.0安装
			今天安装opencms 9.0遇到了一些问题,因为是初次安装和使用,导致耽误了非常多时间.所以在此记录一下以备以后借鉴. 首先附上安装步骤链接: http://www.51testing.com/ht ... 
- AVEVA RVM to 3D PDF
			AVEVA RVM to 3D PDF eryar@163.com RvmTranslator 3D PDF plugin can convert PDMS RVM files to 3D PDF w ... 
- 03006_DOS操作数据乱码解决
			1.我们在dos命令行操作中文时,会报错 insert into sort(sid,sname) values(2,"电视机"); ERROR 1366 (HY000): Inco ... 
- 杭电(hdu)2053 Switch Game 水题
			Switch Game Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ... 
- Linux下的led驱动程序,ok6410
			本程序採用动态映射的方法控制led.硬件平台为飞凌的ok6410 led.h:定义控制命令 #ifndef _LED_H #define _LED_H #define LED_MAGIC 'M' #d ... 
- Filebeat之input和output(包含Elasticsearch Output 、Logstash Output、 Redis Output、 File Output和 Console Output)
			前提博客 https://i.cnblogs.com/posts?categoryid=972313 Filebeat啊,根据input来监控数据,根据output来使用数据!!! Filebeat的 ... 
- Atcoder At Beginner Contest 068 C - Cat Snuke and a Voyage
			C - Cat Snuke and a Voyage Time limit : 2sec / Memory limit : 256MB Score : 300 points Problem State ... 
- 蚂蚁金服入股36Kr给我的一点警示:应该相信自己的理性分析,不能盲目迷信权威
			最近3年,关注互联网和创业投资比较多,每周都会关注下本周发生的创业投融资大事件. 我注意到,一些自媒体作者经常会发布一些有"前瞻性"的文章,比如"美团大众要合并了&quo ... 
