SpringBoot 使用Druid实现数据库连接池并结合AOP实现多数据源

Spring Boot是一个快速开发Spring应用程序的框架,而Druid是一个高性能的数据库连接池,可以提高数据库访问的效率。在Spring Boot中使用Druid作为数据库连接池,可以更好地管理和优化数据库连接,提高应用程序的性能。

同时,为了更好地支持多数据源,我们可以结合AOP实现自动切换数据源。本篇博客将介绍如何在Spring Boot中使用Druid实现数据库连接池,并通过AOP实现多数据源的自动切换。
在这里插入图片描述

一. 配置Druid

在使用Druid之前,我们需要在Spring Boot中配置Druid。我们可以在pom.xml中添加Druid的依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

然后在application.properties中添加以下配置:

# 数据源配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.druid.master.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.datasource.druid.master.username=root
spring.datasource.druid.master.password=root
spring.datasource.druid.slave.enabled=true
spring.datasource.druid.slave.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
spring.datasource.druid.slave.username=root
spring.datasource.druid.slave.password=root
# Druid连接池监控配置
spring.datasource.druid.initialSize=5
spring.datasource.druid.minIdle=10
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.maxEvictableIdleTimeMillis=900000
spring.datasource.druid.rewriteBatchedStatements=true
spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=false
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.webStatFilter.enabled=true
spring.datasource.druid.statViewServlet.enabled=true
spring.datasource.druid.statViewServlet.allow=
spring.datasource.druid.statViewServlet.url-pattern=/druid/*
spring.datasource.druid.statViewServlet.login-username=admin
spring.datasource.druid.statViewServlet.login-password=123456
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=1000
spring.datasource.druid.filter.stat.merge-sql=true
spring.datasource.druid.filter.wall.config.multi-statement-allow=true

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主库数据源
            master:
                url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
                username: root
                password: root
            # 从库数据源
            slave:
                # 从数据源开关/默认关闭
                enabled: true
                url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
                username: root
                password: root
            # 初始连接数
            initialSize: 5
            # 最小连接池数量
            minIdle: 10
            # 最大连接池数量
            maxActive: 20
            # 配置获取连接等待超时的时间
            maxWait: 60000
            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            timeBetweenEvictionRunsMillis: 60000
            # 配置一个连接在池中最小生存的时间,单位是毫秒
            minEvictableIdleTimeMillis: 300000
            # 配置一个连接在池中最大生存的时间,单位是毫秒
            maxEvictableIdleTimeMillis: 900000
            rewriteBatchedStatements: true
            # 配置检测连接是否有效
            validationQuery: SELECT 1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: true
                # 设置白名单,不填则允许所有访问
                allow:
                url-pattern: /druid/*
                # 控制台管理用户名和密码
                login-username: ddz
                login-password: 123456
            filter:
                stat:
                    enabled: true
                    # 慢SQL记录
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true

这里我们配置了一个MySQL数据库连接池,并设置了一些Druid的监控配置。其中,spring.datasource.druid.stat-view-servlet.enabled=true 表示开启Druid的监控功能,spring.datasource.druid.stat-view-servlet.url-pattern=/druid/* 表示Druid的监控地址为 /druid/*。

二. 实现多数据源

接下来,我们需要实现多数据源的切换。在Spring Boot中,我们可以通过AOP来实现数据源的自动切换。我们可以使用@Aspect注解来定义一个切面类,并使用@Around注解来定义切面方法。

首先,我们需要定义一个枚举类,用于存储数据源的名称:

public enum DataSourceType
{
    /**
     * 主库
     */
    MASTER,

    /**
     * 从库
     */
    SLAVE
}

然后,我们定义一个切面类DataSourceAspect,并在切面方法中根据注解参数来切换数据源:

@Aspect
@Order(1)
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.example.demo.annotation.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    	// 从注解中获取需要切换的数据源
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);
        if (ds == null) {
        	// 默认主数据源
            DynamicDataSource.setDataSource(DataSourceType.MASTER);
        } else {
            DynamicDataSource.setDataSource(ds.value());
        }
        try {
            return point.proceed();
        } finally {
        	// 销毁数据源 在执行方法之后
            DynamicDataSource.clearDataSource();
        }
    }
}

在切面方法中,我们首先获取切面方法的注解参数@DataSource,根据注解参数来切换数据源。如果注解参数为空,则默认使用主数据源。

然后,在try代码块中执行业务方法,并在finally代码块中清除数据源。

为了实现数据源的自动切换,我们还需要定义一个DynamicDataSource类,用于切换数据源。在DynamicDataSource类中,我们可以使用ThreadLocal来存储当前线程使用的数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {

     /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
    
 	/**
     * 设置数据源的变量
     */
    public static void setDataSource(DataSourceType dataSourceType) {
        contextHolder.set(dataSourceType);
    }
    
     /**
     * 清空数据源变量
     */
    public static void clearDataSource() {
        contextHolder.remove();
    }
    
 	 /**
     * 确定当前查找关键字。这通常用于检查线程绑定的事务上下文。
     * 允许使用任意键。返回的键需要与存储的查找键类型匹配,如resolveSpecifiedLookupKey方法所解析的。
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return contextHolder.get();
    } 
}

在DynamicDataSource类中,我们定义了一个ThreadLocal变量contextHolder,用于存储当前线程使用的数据源。在setDataSource()方法中,我们可以将当前线程使用的数据源存储到contextHolder变量中。在clearDataSource()方法中,我们可以清除contextHolder变量中存储的数据源。在determineCurrentLookupKey()方法中,我们可以根据contextHolder变量中存储的数据源来选择数据源。

三. 添加注解

最后,我们需要在业务方法中添加@DataSource注解,用于指定数据源。@DataSource注解是一个自定义注解,需要在程序中进行定义。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    DataSourceType value() default DataSourceType.MASTER;
}

在@DataSource注解中,我们可以在方法和类上指定使用哪个数据源。如果不指定数据源,则默认使用主数据源。

四. 业务代码

现在,我们已经完成了使用Druid实现数据库连接池和Druid结合AOP实现多数据源自动切换的实现。

先编写一个逻辑类来通过注解选择不同的数据源。

@Service
public interface UserService {
    /**
     * 新增用户到主库
     */
    @DataSource(DataSourceType.MASTER)
    int insertUserToMaster(User user);

    /**
     * 新增用户到从库
     */
    @DataSource(DataSourceType.SLAVE)
    int insertUserToSlave(User user);
}

五. 测试

下面,我们可以编写一个测试类来验证我们的代码是否正确。

@RunWith(SpringRunner.class)
@SpringBootTest
public class DataSourceTest {
 
    @Autowired
    private UserService userService;
 
    @Test
    public void testDataSource() {
        userService.insertUserToMaster(new User("test1"));
        userService.insertUserToSlave(new User("test2"));
    }
}

在测试类中,我们注入了一个UserService实例,并调用了插入用户的方法。在插入用户时,我们指定了使用不同数据源,从而会往不同的数据源中新增数据。最后,我们可以使用断言来验证测试结果是否正确。

六. 总结

通过以上的步骤,我们已经成功地实现了使用Druid实现数据库连接池和Druid结合AOP实现多数据源自动切换。总结起来,我们需要完成以下步骤:

  1. 添加Druid依赖:在pom.xml文件中添加Druid的依赖。

  2. 配置Druid数据源:在application.properties文件中添加Druid数据源的配置信息。

  3. 定义切面类:定义一个切面类,用于在方法执行前切换数据源,并在方法执行后清除数据源。

  4. 定义自定义注解:定义一个自定义注解@DataSource,用于指定数据源。

  5. 编写业务代码:编写业务代码,并在业务方法上添加@DataSource注解,用于指定数据源。

  6. 编写测试代码:编写测试代码,并使用断言来验证测试结果是否正确。

使用Druid实现数据库连接池和Druid结合AOP实现多数据源自动切换,可以有效地解决在多数据源情况下的数据库连接池和数据源切换问题,同时也可以提高系统的稳定性和性能。

GitHub 加速计划 / druid / druid
27.83 K
8.56 K
下载
阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
最近提交(Master分支:3 个月前 )
7e346071 - 2 天前
33eb0517 - 3 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐