springboot通过aop实现多数据源动态切换

本文详细介绍了如何在SpringBoot项目中实现多数据源的动态切换,包括使用Druid连接池进行配置,通过注解和切面实现数据源的灵活选择,以及Druid监控配置的设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot实现多数据源动态切换(Druid连接池)
1:引入依赖
连接池
切面

2:配置yml
spring:
datasource:
druid:
db1:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
username: root
password: root
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall’用于防火墙,此处是filter修改的地方
filters:
commons-log.connection-logger-name: stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
useGlobalDataSourceStat: true
db2:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dcmonitor?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
username: root
password: root
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall’用于防火墙,此处是filter修改的地方
filters:
commons-log.connection-logger-name: stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
useGlobalDataSourceStat: true
3:配置数据源
/**

  • 多数据源配置类 配置了两个数据源
    /
    @Configuration
    public class DataSourceConfig {
    /
    *

    • 从配置文件配置数据源
    • @return
      */
      @Bean(name = “datasource1”)
      @ConfigurationProperties(prefix = “spring.datasource.druid.db1”) // application.properteis中对应属性的前缀
      public DataSource dataSource1() {
      // return DataSourceBuilder.create().build();
      return new DruidDataSource();
      }

    /**

    • 从配置文件配置数据源
    • @return
      */
      @Bean(name = “datasource2”)
      @ConfigurationProperties(prefix = “spring.datasource.druid.db2”) // application.properteis中对应属性的前缀
      public DataSource dataSource2() {
      // return DataSourceBuilder.create().build();
      return new DruidDataSource();
      }
      @Bean(name = “datasource3”)
      @ConfigurationProperties(prefix = “spring.datasource.druid.db3”) // application.properteis中对应属性的前缀
      public DataSource dataSource3() {
      // return DataSourceBuilder.create().build();
      return new DruidDataSource();
      }

    /**

    • 动态数据源 进行数据源的切换
    • @return
      */
      @Primary
      @Bean(name = “dynamicDataSource”)
      public DataSource dynamicDataSource() {
      DynamicDataSource dynamicDataSource = new DynamicDataSource();
      // 默认数据源
      dynamicDataSource.setDefaultTargetDataSource(dataSource1());
      // 配置多数据源
      Map<Object, Object> dsMap = new HashMap();
      dsMap.put(“datasource1”, dataSource1());
      dsMap.put(“datasource2”, dataSource2());
      dsMap.put(“datasource3”, dataSource3());
      //将多数据源添加到数据源池中
      dynamicDataSource.setTargetDataSources(dsMap);
      return dynamicDataSource;
      }

    /**

    • 通过动态数据源配置sqlsessionfactory
    • @param dynamicDataSource
    • @return
    • @throws Exception
      /
      @Bean
      public SqlSessionFactory sqlSessionFactory(@Qualifier(“dynamicDataSource”) DataSource dynamicDataSource) throws Exception {
      SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
      factoryBean.setDataSource(dynamicDataSource);
      factoryBean.setTypeAliasesPackage(“com.trs.bigscreen.mapper”);
      factoryBean.setMapperLocations(
      new PathMatchingResourcePatternResolver().getResources("classpath:mapping/
      .xml"));
      return factoryBean.getObject();
      }

    /**

    • 通过动态数据源配置sqlsessionTemplate
    • @param sqlSessionFactory
    • @return
    • @throws Exception
      */
      @Bean
      public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
      return new SqlSessionTemplate(sqlSessionFactory);
      }

    /**

    • 配置事务管理器
    • @param dynamicDataSource
    • @return
      */
      @Bean
      public DataSourceTransactionManager transactionManager(@Qualifier(“dynamicDataSource”) DataSource dynamicDataSource){
      return new DataSourceTransactionManager(dynamicDataSource);

    }
    }
    4:创建动态数据源获取类
    public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
    System.out.println(“数据源为”+DataSourceContextHolder.getDB());
    return DataSourceContextHolder.getDB();
    }
    }
    5:创建数据源管理切换类
    public class DataSourceContextHolder {

    /**

    • 默认数据源
      */
      public static final String DEFAULT_DS = “datasource1”;

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

    // 设置数据源名
    public static void setDB(String dbType) {
    System.out.println(“切换到{”+dbType+"}数据源");
    contextHolder.set(dbType);
    }

    // 获取数据源名
    public static String getDB() {
    return (contextHolder.get());
    }

    // 清除数据源名
    public static void clearDB() {
    contextHolder.remove();
    }
    }
    6:Druid配置监控
    //Druid监控配置
    @Configuration
    public class DruidMoniterConfig {
    //1、配置一个管理后台的Servlet
    @Bean
    public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), “/druid/*”);
    Map<String, String> initParams = new HashMap<>();
    initParams.put(“loginUsername”, “admin”);
    initParams.put(“loginPassword”, “admin”);
    initParams.put(“allow”, “”);//默认就是允许所有访问
    // initParams.put(“deny”, “192.168.15.21”);//黑名单的IP

     bean.setInitParameters(initParams);
     return bean;
    

    }

    //2、配置一个web监控的filter
    @Bean
    public FilterRegistrationBean webStatFilter() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new WebStatFilter());

     Map<String, String> initParams = new HashMap<>();
     initParams.put("exclusions", "*.js,*.css,/druid/*");
    
     bean.setInitParameters(initParams);
    
     bean.setUrlPatterns(Arrays.asList("/*"));
    
     return bean;
    

    }

}
7:定义注解用于指定数据源
/**

  • 数据源切换注解
    /
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD,ElementType.TYPE})
    public @interface DS {
    String value() default “datasource1”;
    }
    8:定义切面进行动态切换
    /
    *

  • 动态数据源切面
    */
    @Order(0) // Spring的事务与数据源是绑定的。也就说,如果你开启了事务,那么数据源已经绑定了。那么这个时候,你在去切换数据源就无效了。也就是说要想有效,那么久要在事务开启之前就把数据源切换好。
    @Aspect
    @Component
    public class DynamicDataSourceAspect {

    @Before("@annotation(ds)")
    public void beforeSwitchDS(JoinPoint point,DS ds){
    System.out.println(“进入切面了…”);
    //获得当前访问的class
    Class<?> className = point.getTarget().getClass();
    //获得访问的方法名
    String methodName = point.getSignature().getName();
    //得到方法的参数的类型
    Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
    String dataSource = DataSourceContextHolder.DEFAULT_DS;
    try {
    // 得到访问的方法对象
    Method method = className.getMethod(methodName, argClass);
    // 判断是否存在@DS注解
    if (method.isAnnotationPresent(DS.class)) {
    DS annotation = method.getAnnotation(DS.class);
    // 取出注解中的数据源名
    dataSource = annotation.value();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    // 切换数据源
    DataSourceContextHolder.setDB(dataSource);
    }

    @After("@annotation(com.trs.bigscreen.annotation.DS)")
    public void afterSwitchDS(JoinPoint point){
    DataSourceContextHolder.clearDB();
    }
    }
    9:用法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值