spring boot 多数据源动态切换

来源:互联网 发布:百度云 知乎 编辑:程序博客网 时间:2024/05/31 00:39

总体思路

使用AOP进行数据源切换,继承AbstractRoutingDataSource实现数据源动态的获取,使用注解指定数据源。

//指定aop事务执行顺序,已保证在切换数据源的后面@EnableTransactionManagement(order = 2)//排除数据源自动配置@SpringBootApplication(exclude = {        DataSourceAutoConfiguration.class})

一. 多数据源配置

spring:    datasource:        # 使用druid数据源        druid:          default:            url: jdbc:mysql:/url/database?useUnicode=true&characterEncoding=utf-8            username: xx            password: 123344            driver-class-name: com.mysql.jdbc.Driver            filters: stat            maxActive: 300            initialSize: 5            maxWait: 60000            minIdle: 5            timeBetweenEvictionRunsMillis: 60000            minEvictableIdleTimeMillis: 300000            validationQuery: select 'x'            testWhileIdle: true            testOnBorrow: false            testOnReturn: false            poolPreparedStatements: true            maxOpenPreparedStatements: 20            WebStatFilter:              enabled: true #是否启用StatFilter默认值true              urlPattern: /*              exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/            StatViewServlet:              enabled: true # 是否启用StatViewServlet默认值true              urlPattern: /druid/*          other:            url: jdbc:mysql://url/database1?useUnicode=true&characterEncoding=utf-8            username: xxx            password: 432423            driver-class-name: com.mysql.jdbc.Driver            filters: stat            maxActive: 300            initialSize: 5            maxWait: 60000            minIdle: 5            timeBetweenEvictionRunsMillis: 60000            minEvictableIdleTimeMillis: 300000            validationQuery: select 'x'            testWhileIdle: true            testOnBorrow: false            testOnReturn: false            poolPreparedStatements: true            maxOpenPreparedStatements: 20            WebStatFilter:              enabled: true #是否启用StatFilter默认值true              urlPattern: /*              exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid1/            StatViewServlet:              enabled: true # 是否启用StatViewServlet默认值true              urlPattern: /druid1/*    aop:      proxy-target-class: true

读取配置

package com.***.dataSource;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;import java.util.HashMap;import java.util.Map;/** * 动态数据源配置 * @author Lucian */@Configurationpublic class DynamicDataSourceConfiguration {    @Bean(name = DataSourceConsts.DEFAULT)    @ConfigurationProperties(prefix = "spring.datasource.druid.default")    public DataSource dataSourceOne(){        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();        dataSource.setName(DataSourceConsts.DEFAULT);        return dataSource;    }    @Bean(name = DataSourceConsts.TRADE)    @ConfigurationProperties(prefix = "spring.datasource.druid.trade")    public DataSource dataSourceTwo(){        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();        dataSource.setName(DataSourceConsts.TRADE);        return dataSource;    }    /**     * 动态数据源: 通过AOP在不同数据源之间动态切换     * @return     */    @Bean(name = "dynamicDataSource")    public DataSource dataSource() {        DynamicDataSource dynamicDataSource = new DynamicDataSource();        // 默认数据源        dynamicDataSource.setDefaultTargetDataSource(dataSourceOne());        // 配置多数据源        Map<Object, Object> dsMap = new HashMap(2);        dsMap.put(DataSourceConsts.DEFAULT, dataSourceOne());        dsMap.put(DataSourceConsts.TRADE, dataSourceTwo());        dynamicDataSource.setTargetDataSources(dsMap);        return dynamicDataSource;    }    @Bean    public PlatformTransactionManager txManager(DataSource dynamicDataSource) {        return new DataSourceTransactionManager(dynamicDataSource);    }    @Bean    @ConfigurationProperties(prefix = "mybatis")    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dynamicDataSource) throws Exception {        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();        sqlSessionFactoryBean.setDataSource(dynamicDataSource);        return sqlSessionFactoryBean;    }    @Bean    public SqlSessionFactory sqlSessionFactory(SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {        return sqlSessionFactoryBean.getObject();    }    @Bean    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory); // 使用上面配置的Factory        return template;    }}

二. 数据源动态切换类

package com.hunter.nocardpay.core.dataSource;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import java.lang.reflect.Method;/** * 动态数据源AOP切面 */@Aspect@Order(1)@Componentpublic class DynamicDataSourceAspect {  @Around("execution(* com.hunter.nocardpay.*.service..*(..))")  public Object switchDS(ProceedingJoinPoint point) throws Throwable {    Class<?> className = point.getTarget().getClass();    String dataSource = DataSourceConsts.DEFAULT;    if (className.isAnnotationPresent(DS.class)) {      DS ds = className.getAnnotation(DS.class);      dataSource = ds.value();    }else{      // 得到访问的方法对象      String methodName = point.getSignature().getName();      Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();      Method method = className.getMethod(methodName, argClass);      // 判断是否存在@DS注解      if (method.isAnnotationPresent(DS.class)) {        DS annotation = method.getAnnotation(DS.class);        // 取出注解中的数据源名        dataSource = annotation.value();      }    }    // 切换数据源    DataSourceContextHolder.setDB(dataSource);    try {      return point.proceed();    } finally {      DataSourceContextHolder.clearDB();    }  }}

三. 当前线程数据源

package com.hunter.nocardpay.core.dataSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 当前线程数据源 * @author Lucian */public class DataSourceContextHolder {    public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();    // 设置数据源名    public static void setDB(String dbType) {        log.debug("切换到{}数据源", dbType);        contextHolder.set(dbType);    }    // 获取数据源名    public static String getDB() {        return (contextHolder.get());    }    // 清除数据源名    public static void clearDB() {        contextHolder.remove();    }}

四. 动态数据源

package com.hunter.nocardpay.core.dataSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 动态数据源 * @author Lucian */public class DynamicDataSource extends AbstractRoutingDataSource {    private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);    @Override    protected Object determineCurrentLookupKey() {        log.debug("数据源为:====", DataSourceContextHolder.getDB());        return DataSourceContextHolder.getDB();    }}

数据源注解

package com.hunter.nocardpay.core.dataSource;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 数据源注解 * @author Lucian */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.METHOD})public @interface DS {  String value() default DataSourceConsts.DEFAULT;}
阅读全文
0 0