zoukankan      html  css  js  c++  java
  • spring AbstractRoutingDataSource实现动态数据源切换

    使用Spring 提供的 AbstractRoutingDataSource 实现

    创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:
    public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
    return DataSourceContextHolder.getDatasourceKey();
    }
    }
    xml中配置数据源与事务:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="dynamicDataSource" class="spring.dbs.DynamicDataSource">
    <property name="defaultTargetDataSource" ref="primaryDataSource"/>
    <property name="targetDataSources">
    <map>
    <entry key="primaryDataSource" value-ref="primaryDataSource"></entry>
    <entry key="secondaryDataSource" value-ref="secondaryDataSource"></entry>
    <entry key="thirdlyDataSource" value-ref="thirdlyDataSource"></entry>
    </map>
    </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dynamicDataSource"/>
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <tx:method name="batch*" propagation="REQUIRED"/>
    <tx:method name="add*" propagation="REQUIRED"/>
    <tx:method name="insert*" propagation="REQUIRED"/>
    <tx:method name="persist*" propagation="REQUIRED"/>
    <tx:method name="delete*" propagation="REQUIRED"/>
    <tx:method name="remove*" propagation="REQUIRED"/>
    <tx:method name="merge*" propagation="REQUIRED"/>
    <tx:method name="update*" propagation="REQUIRED"/>
    <tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
    <tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
    <tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
    <tx:method name="load*" read-only="true" propagation="SUPPORTS"/>
    <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
    <tx:method name="*" read-only="true" propagation="SUPPORTS"/>
    </tx:attributes>
    </tx:advice>

    <aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* spring.service..*(..))"/>
    <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
    </aop:config>
    </beans>

    看 DataSourceContextHolder实现,负责处理切换数据源策略的key,使用ThreadLocal确保线程隔离:
    public class DataSourceContextHolder {

    private static final ThreadLocal<String> DATASOURCE_KEY = new ThreadLocal<>();

    /**
    * 设置 datasourceKey
    *
    * @param datasourceKey
    */
    public static void setDatasourceKey(String datasourceKey) {
    DATASOURCE_KEY.set(datasourceKey);
    }

    /**
    * 获取 datasourceKey
    *
    * @return
    */
    public static String getDatasourceKey() {
    return DATASOURCE_KEY.get();
    }

    /**
    * 删除 datasourceKey
    */
    public static void clearDatasourceKey() {
    DATASOURCE_KEY.remove();
    }
    }

    使用Aop实现策略,实现动态切换数据源:
    @Component
    @Aspect
    public class DatasourceHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatasourceHandler.class);

    private static final String[] DATASOURCES = new String[]{"primaryDataSource", "secondaryDataSource", "thirdlyDataSource"};

    ① @Pointcut("execution(* spring.service..*(..))")
    public void datasourcePoint() {
    }

    @Around("datasourcePoint()")
    public Object handler(ProceedingJoinPoint point) throws Throwable {
    LOGGER.info("begin get datasource......");
    Object[] args = point.getArgs();
    if (null != args && args.length > 0) {
    ② String key = String.valueOf(args[0]);
    // 存入key
    ③ DataSourceContextHolder.setDatasourceKey(getKey(key));
    }
    Object result = point.proceed();
    // 清除key
    ④ DataSourceContextHolder.clearDatasourceKey();
    LOGGER.info("end get datasource......");
    return result;
    }

    /**
    * 获取key方法
    *
    * @param key
    * @return
    */
    private String getKey(String key) {
    LOGGER.info("this key is : [ " + key + " ]!");
    CRC32 crc = new CRC32();
    crc.update(key.getBytes(StandardCharsets.UTF_8));
    long result = crc.getValue();
    LOGGER.info("this key's crc32 is : [ " + result + " ]!");
    int index = (int) (result % 3);
    LOGGER.info("this key's module is : [ " + index + " ]!");
    int len = DATASOURCES.length;
    if (index > (len - 1)) {
    index = len - 1;
    }
    LOGGER.info("this key's result module is : [ " + index + " ]!");
    String datasourceKey = DATASOURCES[index];
    LOGGER.info("this datasource key is : [ " + datasourceKey + " ]!");
    return datasourceKey;
    }
    }
    ①所有service包下的方法执行策略;
    ②demo默认使用第一个参数做为key;
    ③使用CRC32取模,获取数据源key,保存到DataSourceContextHolder.ThreadLocal.DATASOURCE_KEY 中,确保不同线程策略正确;
    ④业务逻辑处理完后,清除key;


    使用JdbcTemplate,配置如下:
    @Configuration
    public class JdbcTemplateBean {

    @Autowired
    @Qualifier("dynamicDataSource")
    private DataSource dynamicDataSource;

    @Bean(name = "dynamicJdbcTemplate")
    @Qualifier("dynamicJdbcTemplate")
    public NamedParameterJdbcTemplate primaryJdbcTemplate() {
    return new NamedParameterJdbcTemplate(dynamicDataSource);
    }
    }

    使用过程中发现一问题,当事务配置为 propagation="REQUIRED"时,因为 Aop顺序问题导致先寻找数据源开启事务,后执行数据源切换策略,此时使用 <aop:advisor order="2"/>属性配置事务顺序为2,并使用 org.springframework.core.Ordered接口,设置策略执行顺序为1,保证策略执行在寻找数据源之前,具体如下:
    事务切面配置:
    <aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* spring.service..*(..))"/>
    <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" order="2"/>
    </aop:config>
    策略执行切面:
    @Component
    @Aspect
    public class DatasourceHandler implements Ordered {

    @Pointcut("execution(* spring.service..*(..))")
    public void datasourcePoint() {
    }

    @Around("datasourcePoint()")
    public Object handler(ProceedingJoinPoint point) throws Throwable {
    // 策略实现
    }


    @Override
    public int getOrder() {
    return 1;
    }
    }

  • 相关阅读:
    composer npm bower 版本依赖符号说明
    FastAdmin 速极后台框架从 v1.0 到 v1.2 的数据库升级
    FastAdmin 也可以出书了
    FastAdmin 开发时用到的 Git 命令 (2020-09-26)
    FastAdmin用什么弹窗组件
    笔记:Linux 文件权限
    笔记:使用源代码在 Centos 7 安装 Git 2
    php gd 生成表格 图片
    easyui datagrid 清空
    mysql 去重
  • 原文地址:https://www.cnblogs.com/frinder6/p/6223272.html
Copyright © 2011-2022 走看看