Spring Boot2.x + Druid动态数据源切换

Spring Boot2.x + Druid动态数据源切换

1、数据源配置

<!-- alibaba的druid数据库连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.9</version>
		</dependency>

2、创建用来保存数据源名字的线程安全的类。通过ThreadLocal栈封闭方式保证线程安全。

public class DataSourceContextHolder {
    public static final String Mater = "master";
    public static final String Slave1 = "slave1";
 
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
 
    public static void setDataSource(String name){
        contextHolder.set(name);
    }
 
    public static String getDataSource(){
        return contextHolder.get();
    }
 
    public static void cleanDataSource(){
        contextHolder.remove();
    }
 
}

3、实现AbstractRoutingDataSource并重写determineCurrentLookupKey()方法

public class DynamicDataSource extends AbstractRoutingDataSource {
 
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

AbstractRoutingDataSource类源码分析,通过resolvedDataSources中以map形式保存着多个数据源。通过重写的determineCurrentLookupKey()来动态的获得当前线程需要的数据源名字。

@Override
	public Connection getConnection() throws SQLException {
		return determineTargetDataSource().getConnection();
	}
 
protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

4、配置其他基础数据源

@Configuration
public class MultipleDateSourceConfig {
 
    /**
     *
     * @return
     */
    @Bean("master")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource creeateMasterDataSource(){
        return new DruidDataSource();
    }
 
    @Bean("slave1")
    @ConfigurationProperties(prefix = "spring.datasource.slave1")
    public DataSource creeateSlave1DataSource(){
        return new DruidDataSource();
    }
 
    /**
     * 设置动态数据源,通过@Primary 来确定主DataSource
     * @return
     */
    @Bean
    @Primary
    public DataSource createDynamicdataSource(@Qualifier("master") DataSource master,@Qualifier("slave1") DataSource slave1){
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(master);
        //配置多数据源
        Map<Object, Object> map = new HashMap<>();
        map.put("master",master);
        map.put("slave1",slave1);
        dynamicDataSource.setTargetDataSources(map);
        return  dynamicDataSource;
    }
}

注意:

1.@Bean创建类的名字默认方法名,指定bean名字通过value或name属性。

2.createDynamicdataSource()中DataSource默认按类型注入,但是会报错。需要通过@Qualifier来指定注入bean的名字。往mybatis的sqlSessionFactory类注入DataSource是按类型注入。

3.通过@Primary注解来说明优先注入此bean。

上述的@ConfigurationProperties(prefix = “spring.datasource.master”)注解从application.yml读取配置信息并注入

spring:
  datasource:
    #type: com.alibaba.druid.pool.DruidDataSource
    master:
      type: com.alibaba.druid.pool.DruidDataSource
      url: jdbc:mysql://ip:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
      username: root
      password: test
      name: master
      # 监控统计拦截的filters 有stat,wall,log4j
      filters: stat 
    slave1:
      type: com.alibaba.druid.pool.DruidDataSource
      url: jdbc:mysql://ip:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
      username: root
      password: 123456
      name: slave1
      filters: stat

注意:添加filters的stat属性后,我们可以在localhost:8080/druid/sql.html 看到Druid对sql的分析管理信息

我们是手动配置DataSource,需要注解掉springboot的自动数据源配置类DataSourceAutoConfiguration

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan("com.test.mspringboot.mapper") //扫描mapper包
public class MSpringBootApplication {
	public static void main(String[] args) {
		SpringApplication.run(MSpringBootApplication.class, args);
	}
}

5、动态切换配置

通过自定义注解+AOP的方式来实现动态数据源的切换

引入jar

<!--aop-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

自定义注解

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

定义切面

@Aspect
@Component
@Order(1) //需要加入切面排序
public class DynamicDataSourceAspect {
    private Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
 
    //切入点只对@Service注解的类上的@DataSource方法生效
    @Pointcut(value="@within(org.springframework.stereotype.Service) && @annotation(dataSource)" )
    public void dynamicDataSourcePointCut(DataSource dataSource){}
 
    @Before(value = "dynamicDataSourcePointCut(dataSource)")
    public void switchDataSource(DataSource dataSource) throws Throwable{
        logger.info("##############数据源 :{}###############",dataSource.value());
        DataSourceContextHolder.setDataSource(dataSource.value());
    }
 
    @After(value="dynamicDataSourcePointCut(dataSource)")
    public void after(DataSource dataSource){
        DataSourceContextHolder.cleanDataSource();
    }
}

注意:需要使用@Order(1) 注解,保证数据源的切换在数据源的获取之前。

6、使用

@Service
public class UserServiceImpl implements UserService {
 
    @Autowired
    private UserMapper userMapper;
 
    @DataSource(DataSourceContextHolder.Slave1)
    @Transactional
    @Override
    public User searchUserById(int id) throws Exception {
        return userMapper.selectByPrimaryKey(id);
    }
 
}

原创:https://blog.csdn.net/zl_momomo/article/details/82851134

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值