SpringBoot + 动态数据源切换

学习动态数据源切换,个人查了很多资料及demo,在此总结一下,只为加深印象,以及请各大佬指点。谢谢!!!

重点思想:动态数据源是基于Spring的AOP思想,在调用默认数据源之前切入实现的。

个人详情Demo,可移驾github:

         https://github.com/WenFeiSun/SpringBoot

本人SpringBoot项目框架已经搭建完成。

 

第一步,导入MySQl,数据连接池依赖

<!--阿里数据库连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.14</version>
</dependency>
<!-- mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.18</version>
</dependency>

第二步,配置数据源

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

第三步,定义多数据源切面类
@Aspect //作用是把当前类标识为一个切面供容器读取      切面(Aspect
@Order(1) //加载顺序    越小越提前加载
@Component
public class DataSourceAspect
{
    protected Logger logger = LoggerFactory.getLogger(getClass());
    @Pointcut("@annotation(com.sun.control.web.common.annotation.DataSource)")    //@annotation是针对方法的注解   切入点(Pointcut)
    public void dsPointCut()
    {
    }
    @Around("dsPointCut()")//当需要改变目标方法的返回值时,只能使用Around方法;
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if (dataSource != null) {
            Object[] args = point.getArgs();
            if (args.length > 0 && args[0] != null) {
                String dataSourceId = args[0].toString();
                DynamicDataSourceContextHolder.setDataSourceType(dataSourceId);
            }
        }
        try
        {
            return point.proceed();
        }
        finally
        {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}

第四步,创建数据源上下文分配对象:DynamicDataSourceContextHolder

public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

第五步,实现路由切换AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey()
    {
        /**
         * DynamicDataSourceContextHolder代码中使用setDataSourceType
         * 设置当前的数据源,在路由类中使用getDataSourceType进行获取,
         *  交给AbstractRoutingDataSource进行注入使用。
         */
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

第六步,创建多数据源

@Component
public class DataSourceConfig {

    @Autowired
    private DruidProperties druidProperties;
    @Autowired
    private DynamicDataSource dynamicDataSource;
    @Autowired
    private DataBaseDriverConfig dataBaseDriverConfig;

    @PostConstruct//项目启动既加载
    public void loadCustDataSource(){
        List<DsDatasource> dsDatasources = new ArrayList<>();//此处为数据源集合
        DsDatasource dsDatasource1 = new DsDatasource();
        dsDatasource1.setDatasourceId("sunwenfei123");
        dsDatasource1.setDatabaseType(9);
        dsDatasource1.setDatabaseUrl("jdbc:mysql://localhost:3306/XIAO_QU1");
        dsDatasource1.setDatabaseAccount("root");
        dsDatasource1.setDatabasePassword("abc123");
        dsDatasources.add(dsDatasource1);
        Map<Object, Object> targetDataSources = new HashMap<>();
        for (DsDatasource dsDatasource : dsDatasources) {
            DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
            String driverClassName = dataBaseDriverConfig.getDriver().get(dsDatasource.getDatabaseType() + "");
            dataSource.setDriverClassName(driverClassName);
            dataSource.setUrl(dsDatasource.getDatabaseUrl());
            dataSource.setUsername(dsDatasource.getDatabaseAccount());
            dataSource.setPassword(dsDatasource.getDatabasePassword());
            DataSource ds = druidProperties.dataSource(dataSource);
            targetDataSources.put(dsDatasource.getDatasourceId(), ds);
        }
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.afterPropertiesSet();
    }
}

最后要注意,启动类一定要加exclude = { DataSourceAutoConfiguration.class },要不然会出错。

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) //禁用数据源自动配置
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值