虚位以待(AD)
虚位以待(AD)
首页 > 软件编程 > Java编程 > SpringBoot AOP方式实现多数据源切换的方法

SpringBoot AOP方式实现多数据源切换的方法
类别:Java编程   作者:码皇   来源:互联网   点击:

本篇文章主要介绍了SpringBoot AOP方式实现多数据源切换的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑把这个查询走从库。所以涉及到需要在一个项目中配置多数据源,并且能够动态切换。经过一番摸索,完美实现动态切换,记录一下配置方法供大家参考。

设计总体思路

Spring-Boot+AOP方式实现多数据源切换,继承AbstractRoutingDataSource实现数据源动态的获取,在service层使用注解指定数据源。

步骤

一、多数据源配置

在application.properties中,我们的配置是这样的

    #主数据源druid.master.url=jdbc:mysql://url/masterdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNulldruid.master.username=xxxdruid.master.password=123druid.master.driver-class-name=com.mysql.jdbc.Driverdruid.master.max-wait=5000druid.master.max-active=100druid.master.test-on-borrow=truedruid.master.validation-query=SELECT 1#从数据源druid.slave.url=jdbc:mysql://url/slavedb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNulldruid.slave.username=xxxdruid.slave.password=123druid.slave.driver-class-name=com.mysql.jdbc.Driverdruid.slave.max-wait=5000druid.slave.max-active=100druid.slave.test-on-borrow=truedruid.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执行顺序(需要在事务之前,否则事务只发生在默认库中)@Componentpublic 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实现动态切换了

    @Componentpublic class xxxServiceImpl {
    @Resource private XxxMapperExt xxxMapperExt;
    @MyDataSource(value= DataSourceType.Slave) public List<Object> getAll(){
    return xxxMapperExt.getAll();
    }
    }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

  • SpringBoot项目中使用AOP的方法
  • Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验 使用消息资源文件对消息国际化
  • 浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)
  • 详解SpringBoot之集成Spring AOP
  • 详解SpringBoot AOP 拦截器(Aspect注解方式)
相关热词搜索: SpringBoot AOP多数据源切换 springboot切