Fork me on GitEE

基于AbstractRoutingDataSource实现动态切换数据源

基于AbstractRoutingDataSource实现动态切换数据源

/**
 * DataSource
注解接口
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceAnnotation {
    String value() default DataSourceSelect.MASTER;
}

 


/**
 *
注解前后增强获取数据库清空数据库
 */
@Aspect
@Component
@Logger
public class DatasourceAspect {
@Pointcut("@annotation(com.example.demo.config.DataSourceAnnotation)")
public void pointcut() {
}
    @Before("pointcut()")
    public void beforeExecute(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        DataSourceAnnotation annotation = method.getAnnotation(DataSourceAnnotation.class);
        if (annotation == null) {
            annotation = joinPoint.getTarget().getClass().getAnnotation(DataSourceAnnotation.class);
             DynamicDataSource.setDataSource(DataSourceSelect.MASTER);
        }else{
            // 切换数据源
            DynamicDataSource.setDataSource (annotation.value());
        }
    }
    @After("pointcut()")
    public void afterExecute() {
        DynamicDataSource.clear();
    }
}

 

定义数据源类来存储多个数据源选择

public interface DataSourceSelect {
    /**
     *
主库源
     */
   
String MASTER="master";
    /**
     *
从库源
     */
   
String  SLAVE ="slave";

}

 

继承AbstractRoutingDataSource重写方法实现把默认数据源和目标数据源注入

并提供ThreadLocal线程保证数据源隔离。

public class DynamicDataSource extends AbstractRoutingDataSource {
    private final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
    private static final ThreadLocal<String> DATA_SOURCE_CONTEXT_HOLDER = new ThreadLocal<>();

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }
     static void setDataSource(String dataSource) {
        DATA_SOURCE_CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return DATA_SOURCE_CONTEXT_HOLDER.get();
    }

     static void clear() {
        DATA_SOURCE_CONTEXT_HOLDER.remove();
    }

}

 

 

把数据源配置路径与数据源匹配,并利用子类调用有参构造调用父类AbstractRoutingDataSource的方法把数据源信息加载进datasource

 

/**
 *
配置多数据源
 * @author xiaohe
 * @version V1.0.0
 */
@Configuration
public class DynamicDataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource(){

        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource(){

        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>(5);
        targetDataSources.put(DataSourceSelect.MASTER, masterDataSource);
        targetDataSources.put(DataSourceSelect.SLAVE, slaveDataSource);
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

}

Application启动项,exclude = DataSourceAutoConfiguration.class排除自动注入数据源的配置,加上Import 用来加加载我们定义的datasource。

 
@ComponentScan(basePackages ="com.example.demo.*")
@EnableJpaRepositories("com.example.demo.repository")
@Import(DynamicDataSourceConfig.class)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class DruidApplication {
    public static void main(String[] args) {
        SpringApplication.run(DruidApplication.class, args);
    }
}

 

FQA:

9. 注解没有被注入到ioc容器导致注解失效。

 

10. 多数据源的动态切换,在程序运行时,把数据源数据源动态织入到程序中,灵活的进行数据源切换。
- 基于多数据源的动态切换,我们可以实现读写分离,这么做缺点也很明显,无法动态的增加数据源。

只支持单库事务,也就是说切换数据源要在开启事务之前执行。

spring DataSourceTransactionManager进行事务管理,开启事务,会将数据源缓存到DataSourceTransactionObject对象中进行后续的commit rollback等事务操作。

posted @ 2019-09-16 17:14  问道于盲  阅读(390)  评论(0编辑  收藏  举报