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。
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,但是值是固定的 ...
随机推荐
- ipython的安装
ipython:是python的一个交互式shell环境,可以安装到windows和linux上面.作用:用来执行python代码和调试用.windows上面安装:分为2.x版本和3.x版本,分为py ...
- Avalon接口协议
Avalon接口协议 https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/manual/mnl_ava ...
- 一年成为emacs高手
http://blog.csdn.net/redguardtoo/article/details/7222501
- dfs 翻棋盘end
#include<iostream> char data[16]; int a[16]; int d[4] = { -4, 1, 4, -1 }; char b[16]; int flag ...
- Generics and Collection (1)
public static void main(String args[]) { List ints = Arrays.asList(), )}); ; for(Iterator it = ints. ...
- HDU 5410 CRB and His Birthday ——(完全背包变形)
对于每个物品,如果购买,价值为A[i]*x+B[i]的背包问题. 先写了一发是WA的= =.代码如下: #include <stdio.h> #include <algorithm& ...
- Python学习之路——基础篇(1)字符串格式化
字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存. 百分号方式 ...
- TimeQuest学习
1.物理时钟特性:clock skew(时钟差),jitter(拉动),clock latency(时钟潜伏),这些物理时钟特性又称为uncertainl--非定性,或非理想性. clock skew ...
- oracle exp imp 导入 正在跳过表 plsql 导入表 成功终止 数据 被导入
http://blog.csdn.net/agileclipse/article/details/12968011 .导入过程中,所有表导入都出现提示, 正在跳过表...某某表名 最后提示成功终止导入 ...
- oracle java SE
http://www.oracle.com/technetwork/java/javase/downloads/index.html