SpringBoot AOP方式实现多数据源切换的方法
最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑把这个查询走从库。所以涉及到需要在一个项目中配置多数据源,并且能够动态切换。经过一番摸索,完美实现动态切换,记录一下配置方法供大家参考。
设计总体思路
spring-boot+aop方式实现多数据源切换,继承abstractroutingdatasource实现数据源动态的获取,在service层使用注解指定数据源。
步骤
一、多数据源配置
在application.properties中,我们的配置是这样的
#主数据源 druid.master.url=jdbc:mysql://url/masterdb?useunicode=true&characterencoding=utf8&zerodatetimebehavior=converttonull druid.master.username=xxx druid.master.password=123 druid.master.driver-class-name=com.mysql.jdbc.driver druid.master.max-wait=5000 druid.master.max-active=100 druid.master.test-on-borrow=true druid.master.validation-query=select 1 #从数据源 druid.slave.url=jdbc:mysql://url/slavedb?useunicode=true&characterencoding=utf8&zerodatetimebehavior=converttonull druid.slave.username=xxx druid.slave.password=123 druid.slave.driver-class-name=com.mysql.jdbc.driver druid.slave.max-wait=5000 druid.slave.max-active=100 druid.slave.test-on-borrow=true druid.slave.validation-query=select 1
读取配置
<!-- master数据源 --> <bean primary="true" id="masterdb" class="com.alibaba.druid.pool.druiddatasource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="driverclassname" value="com.mysql.jdbc.driver"/> <property name="url" value="${druid.master.url}"/> <property name="username" value="${druid.master.username}"/> <property name="password" value="${druid.master.password}"/> <!-- 配置初始化最大 --> <property name="maxactive" value="${druid.master.max-active}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxwait" value="${druid.master.max-wait}"/> <property name="validationquery" value="${druid.master.validation-query}"/> <property name="testonborrow" value="${druid.master.test-on-borrow}"/> </bean> <!-- slave数据源 --> <bean primary="true" id="slavedb" class="com.alibaba.druid.pool.druiddatasource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="driverclassname" value="com.mysql.jdbc.driver"/> <property name="url" value="${druid.slave.url}"/> <property name="username" value="${druid.slave.username}"/> <property name="password" value="${druid.slave.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="maxactive" value="${druid.slave.max-active}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxwait" value="${druid.slave.max-wait}"/> <property name="validationquery" value="${druid.slave.validation-query}"/> <property name="testonborrow" value="${druid.slave.test-on-borrow}"/> </bean> <!-- 动态数据源,根据service接口上的注解来决定取哪个数据源 --> <bean id="datasource" class="datasource.dynamicdatasource"> <property name="targetdatasources"> <map key-type="java.lang.string"> <entry key="slave" value-ref="slavedb"/> <entry key="master" value-ref="masterdb"/> </map> </property> <property name="defaulttargetdatasource" ref="masterdb"/> </bean> <!-- spring jdbctemplate --> <bean id="jdbctemplate" class="org.springframework.jdbc.core.jdbctemplate"> <property name="datasource" ref="datasource" /> </bean> <!-- spring事务管理器 --> <bean id="transactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager"> <property name="datasource" ref="datasource" /> </bean> <bean id="transactiontemplate" class="org.springframework.transaction.support.transactiontemplate"> <property name="transactionmanager" ref="transactionmanager"/> </bean> <tx:annotation-driven transaction-manager="transactionmanager" proxy-target-class="true" order="2" /> <!-- depositdbsqlsessionfactory --> <bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean"> <property name="datasource" ref="datasource" /> <property name="mapperlocations" value="classpath*:mapper-xxdb/*mapper*.xml" /> </bean> <bean class="org.mybatis.spring.mapper.mapperscannerconfigurer"> <property name="basepackage" value="xxdb.mapper"/> <property name="sqlsessionfactorybeanname" value="sqlsessionfactory"/> </bean>
二、动态数据源
spring为我们提供了abstractroutingdatasource,即带路由的数据源。继承后我们需要实现它的determinecurrentlookupkey(),该方法用于自定义实际数据源名称的路由选择方法,由于我们将信息保存到了threadlocal中,所以只需要从中拿出来即可。
public class dynamicdatasource extends abstractroutingdatasource { private logger logger = loggerfactory.getlogger(this.getclass()); @override protected object determinecurrentlookupkey() { string datasource = jdbccontextholder.getdatasource(); logger.info("数据源为{}",datasource); return datasource; } }
三. 数据源动态切换类
动态数据源切换是基于aop的,所以我们需要声明一个aop切面,并在切面前做数据源切换,切面完成后移除数据源名称。
@aspect @order(1) //设置aop执行顺序(需要在事务之前,否则事务只发生在默认库中) @component public class datasourceaspect { private logger logger = loggerfactory.getlogger(this.getclass()); //切点 @pointcut("execution(* com.xxx.service.*.*(..))") public void aspect() { } @before("aspect()") private void before(joinpoint point) { object target = point.gettarget(); string method = point.getsignature().getname(); class<?> classz = target.getclass();// 获取目标类 class<?>[] parametertypes = ((methodsignature) point.getsignature()) .getmethod().getparametertypes(); try { method m = classz.getmethod(method, parametertypes); if (m != null && m.isannotationpresent(mydatasource.class)) { mydatasource data = m.getannotation(mydatasource.class); logger.info("method :{},datasource:{}",m.getname() ,data.value().getname()); jdbccontextholder.putdatasource(data.value().getname());// 数据源放到当前线程中 } } catch (exception e) { logger.error("get datasource error ",e); //默认选择master jdbccontextholder.putdatasource(datasourcetype.master.getname());// 数据源放到当前线程中 } } @afterreturning("aspect()") public void after(joinpoint point) { jdbccontextholder.cleardatasource(); } }
四、数据源管理类
public class jdbccontextholder { private final static threadlocal<string> local = new threadlocal<>(); public static void putdatasource(string name) { local.set(name); } public static string getdatasource() { return local.get(); } public static void cleardatasource() { local.remove(); } }
五、数据源注解和枚举
我们切换数据源时,一般都是在调用具体接口的方法前实现,所以我们定义一个方法注解,当aop检测到方法上有该注解时,根据注解中value对应的名称进行切换。
@retention(retentionpolicy.runtime) @target(elementtype.method) public @interface mydatasource { datasourcetype value(); } public enum datasourcetype { // 主表 master("master"), // 从表 slave("slave"); private string name; private datasourcetype(string name) { this.name = name; } public string getname() { return name; } public void setname(string name) { this.name = name; } }
六、切点注解
由于我们的动态数据源配置了默认库,所以如果方法是操作默认库的可以不需要注解,如果要操作非默认数据源,我们需要在方法上添加@mydatasource("数据源名称")注解,这样就可以利用aop实现动态切换了
@component public class xxxserviceimpl { @resource private xxxmapperext xxxmapperext; @mydatasource(value= datasourcetype.slave) public list<object> getall(){ return xxxmapperext.getall(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 2019以“智联世界 无限可能”为主题世界人工智能大会召开
下一篇: php读取csc文件并输出
推荐阅读
-
SpringBoot AOP方式实现多数据源切换的方法
-
通过AOP实现MyBatis多数据源的动态切换实例教程
-
SpringBoot使用AOP+注解实现简单的权限验证的方法
-
SpringBoot多配置切换的配置方法
-
SpringBoot AOP方式实现多数据源切换的方法
-
SpringBoot+AOP构建多数据源的切换实践
-
详细聊聊SpringBoot中动态切换数据源的方法
-
SpringBoot+Mybatis+Durid整合多数据源的三种方式,第三种(注解切换)
-
springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)
-
springBoot启动时让方法自动执行的几种实现方式