SpringBoot+druid+aop动态数据源切换实现-修改中

目录

一、核心类AbstractRoutingDataSource

二、具体代码实现过程

1、自定义数据源类DynamicDataSource 

2、切换操作类DynamicDataSourceHolder

3、配置动态数据源DynamicDataSourceConfig

4、定义注解DataSource

5、数据源切换切面DataSourceAspect

6、修改启动类->排除自动配置

三、附录相关配置文件


一、核心类AbstractRoutingDataSource

Spring boot提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。

实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。 

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

对于该抽象类,关注两组变量和一个方法:

  1. Map<Object, Object> targetDataSourcesObject defaultTargetDataSource
  2. Map<Object, DataSource> resolvedDataSourcesDataSource resolvedDefaultDataSource
  3. protected abstract Object determineCurrentLookupKey();

其中两组变量是相互对应的,在熟悉多实例数据源切换代码时不难发现,当有多个数据源的时候,一定要指定一个作为默认的数据源,在这里也同理,当同时初始化多个数据源的时候,需要显示的调用setDefaultTargetDataSource方法指定一个作为默认数据源; 

我们需要关注的是

Map<Object, Object> targetDataSourcesMap<Object, DataSource> resolvedDataSources

targetDataSources是暴露给外部程序用来赋值的,用来添加多个数据源实例(DataSource),而resolvedDataSources是程序内部执行时把targetDataSources赋值到resolvedDataSources,因此会有一个赋值的操作,如下图所示:

根据这段源码可以看出,每次执行时,都会遍历targetDataSources内的所有元素并赋值给resolvedDataSources;这样如果我们在外部程序新增一个新的数据源,都会添加到内部使用,从而实现数据源的动态加载。

继承该抽象类的时候,必须实现一个抽象方法:

protected abstract Object determineCurrentLookupKey()

该方法用于指定到底需要使用哪一个数据源。

到此基本上清楚了该抽象类的使用方法,接下来贴下具体的实现代码

二、具体代码实现过程

1、自定义数据源类DynamicDataSource 

自定义数据源DataSource类:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @Description 自定义动态数据源类
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSourceType();
    }
}

2、切换操作类DynamicDataSourceHolder

通过ThreadLocal维护一个全局唯一的map来实现数据源的动态切换

public class DynamicDataSourceHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}

3、配置动态数据源DynamicDataSourceConfig

Druid数据源加载过程图示

对自定义数据源进行配置,在dynamicDataSource()方法上加@Primary注解,优先使用我们自定义的数据源

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 动态数据源配置类
 */
@Configuration // 配置类
public class DynamicDataSourceConfig {

    private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceConfig.class);

    @Bean//(initMethod = "init") // 初始化加载
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DruidDataSource masterDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean//(initMethod = "init") // 初始化加载
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DruidDataSource slaveDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Primary//被注解为@Primary的Bean将作为首选者
    @Bean
    public DataSource dynamicDataSource(DataSource masterDataSource ,DataSource slaveDataSource){
        Map<Object,Object> targetDataSource = new HashMap<>();
        targetDataSource.put("master",masterDataSource);
        targetDataSource.put("slave",slaveDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSource); // 目标数据源列表
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource); // 默认数据源
        // 数据源刷入
        // super.afterPropertiesSet();
        return dynamicDataSource;
    }

    /**
     * 将动态数据源添加到事务管理器中,并生成新的bean——>待完善
     * @return the platform transaction manager
     */
//    @Bean
//    public PlatformTransactionManager transactionManager() {
//        return new DataSourceTransactionManager(dynamicDataSource(masterDataSource(),slaveDataSource()));
//    }
}

4、定义注解DataSource

设置动态路由的数据源注解

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String value() default "master";
}

5、数据源切换切面DataSourceAspect

使用注解来实现可动态路由的数据源,在每次数据库查询操作前执行

@Aspect
@Component
@Order(-1) // aop顺序,该切面应当先于 @Transactional 执行
public class DataSourceAspect {

    @Pointcut("@annotation(com.swadian.spring.dynamicDataSource.DataSource)")
    public void dataSourcePointCut(){
        // do nothing
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource annotation = method.getAnnotation(DataSource.class);
        if(annotation.value() != null){
            DynamicDataSourceHolder.setDataSourceType(annotation.value());
        }else{
            DynamicDataSourceHolder.setDataSourceType("master");
        }
        try {
            return point.proceed();
        } finally {
            // 清除本次key
            DynamicDataSourceHolder.clearDataSourceType();
        }
    }
}

6、修改启动类->排除自动配置

启动类添加 exclude = {DataSourceAutoConfiguration.class}, 以禁用数据源默认自动配置。

数据源默认自动配置会读取 spring.datasource.* 的属性创建数据源,所以要禁用以进行定制。

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(MyApplication.class);
        try {
            SpringApplication.run(MyApplication.class);
            logger.info("springBoot启动成功...");
        } catch (Exception e) {
            logger.info("SpringBoot启动失败...");
        }
    }
}

三、附录相关配置文件

application.properties配置文件示例:

#数据源1
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.url=jdbc:mysql://localhost:3306/springbootdemo?useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=root
#数据源2
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.url=jdbc:mysql://localhost:3306/seckill?useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=root

#映射器,Mapper包下所有的xml文件
mybatis.mapper-locations=classpath:mapper/*.xml
#开启驼峰模式
mybatis.configuration.map-underscore-to-camel-case=true

pom.xml相关依赖示例:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>
        <!--切面AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.18</version>
        </dependency>
    </dependencies>

参考资料:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 是一个用于快速构建 Java 应用程序的框架。它可以与多种其他框架和组件进行整合,以实现更丰富的功能。在这里,我们将讨论如何使用 Spring Boot 整合 Druid、MyBatis、JTA 分布式事务以及多数据源,同时使用 AOP 注解实现动态切换。 首先,我们可以在 Spring Boot 集成 Druid 数据源Druid 是一个高性能的 JDBC 连接池,可以提供监控和统计功能。我们可以通过在 pom.xml 文件添加相关的依赖,并在 application.properties 文件配置数据源信息,来实现 Druid 的集成。 接下来,我们可以整合 MyBatis 框架,它是一种优秀的持久化解决方案。我们可以使用 MyBatis 来操作数据库,并将其与 Druid 数据源进行整合。为此,我们需要在 pom.xml 文件添加 MyBatis 和 MyBatis-Spring 的依赖,并配置 MyBatis 的相关配置文件。 此外,我们还可以使用 JTA(Java Transaction API)实现分布式事务。JTA 可以在分布式环境协调多个参与者的事务操作。我们可以在 pom.xml 文件添加 JTA 的依赖,并在 Spring Boot 的配置文件配置 JTA 的相关属性,以实现分布式事务的支持。 在实现数据源时,我们可以使用 Spring Boot 的 AbstractRoutingDataSource 来实现动态切换数据源。这个类可以根据当前线程或其他条件选择不同的数据源来进行数据操作。我们可以通过继承 AbstractRoutingDataSource 并实现 determineCurrentLookupKey() 方法来指定当前数据源的 key。然后,在配置文件配置多个数据源,并将数据源注入到 AbstractRoutingDataSource ,从而实现动态切换。 最后,我们可以使用 AOP(Aspect Oriented Programming)注解来实现动态切换AOP 是一种编程范式,可以通过在代码插入特定的切面(Aspect)来实现横切关注点的处理。我们可以在代码使用注解来标记需要切换数据源的方法,然后使用 AOP 技术来拦截这些方法,并根据注解指定的数据源信息来进行数据源切换。 综上所述,通过整合 Druid、MyBatis、JTA 分布式事务以及多数据源,并使用 AOP 注解实现动态切换,我们可以在 Spring Boot 实现强大而灵活的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值