美文网首页
通过注解配置多数据源切换

通过注解配置多数据源切换

作者: 忄斤丨忄寿 | 来源:发表于2016-12-09 09:59 被阅读0次

    配置

    <!-- 第一个数据源 -->
    <bean id="dataSource01" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <constructor-arg>
          <bean class="com.zaxxer.hikari.HikariConfig">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <!-- <property name="autoCommit" value="true"/> -->
            <property name="poolName" value="dataSource01"/>
            <property name="connectionTimeout" value="${jdbc.connectionTimeout:30000}"/>
            <property name="idleTimeout" value="${jdbc.idleTimeout:600000}"/>
            <property name="maxLifetime" value="${jdbc.maxLifetime:1800000}"/>
            <property name="maximumPoolSize" value="${jdbc.maximumPoolSize:100}"/>
            <property name="minimumIdle" value="${jdbc.minimumIdle:10}"/>
            <!-- <property name="metricRegistry" ref="metric"/> -->
            <!-- <property name="healthCheckRegistry" ref="healthCheckRegistry"></property>-->
            <property name="connectionTestQuery" value="SELECT 1"/>
          </bean>
        </constructor-arg>
    </bean>
    
    <!-- 第二个数据源 -->
    <bean id="dataSource02" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <constructor-arg>
          <bean class="com.zaxxer.hikari.HikariConfig">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <!-- <property name="autoCommit" value="true"/> -->
            <property name="poolName" value="risk"/>
            <property name="connectionTimeout" value="${jdbc.connectionTimeout:30000}"/>
            <property name="idleTimeout" value="${jdbc.idleTimeout:600000}"/>
            <property name="maxLifetime" value="${jdbc.maxLifetime:1800000}"/>
            <property name="maximumPoolSize" value="${jdbc.maximumPoolSize:100}"/>
            <property name="minimumIdle" value="${jdbc.minimumIdle:10}"/>
            <!-- <property name="metricRegistry" ref="metric"/> -->
            <!-- <property name="healthCheckRegistry" ref="healthCheckRegistry"></property>-->
            <property name="connectionTestQuery" value="SELECT 1"/>
          </bean>
        </constructor-arg>
    </bean>
    
    <!-- 自定义数据源导航类,根据key值返回对应的数据源 -->
    <!-- 配置动态切换数据源 -->
    <bean id="dataSource" class="com.xxxx.xxxx.xxxx.router.DynamicDataSource">
        <property name="targetDataSources">
          <map key-type="java.lang.String">
            <entry key="dataSource01" value-ref="dataSource01"></entry>
            <entry key="dataSource02" value-ref="dataSource02"></entry>
          </map>
        </property>
        <!-- 默认目标数据源为你主库数据源 -->
        <property name="defaultTargetDataSource" ref="dataSource01"/>
    </bean>
    
    <!-- 配置切面 -->
    <bean id="dataSourceAspect" class="com.xxxx.xxxx.xxxx.router.DataSourceAspect"/>
    
    <!-- AOP动态切换数据源 -->
    <aop:config>
        <aop:aspect ref="dataSourceAspect" order="1">
          <!-- 拦截对应的业务方法 -->
          <aop:pointcut id="dataSourcePointcut" expression="execution(public * com.xxxx.xxxx.xxx.service.impl.*.*(..))"/>
          <!-- 通过AOP对注解进行验证 -->
          <aop:before pointcut-ref="dataSourcePointcut" method="intercept"/>
        </aop:aspect>
    </aop:config>
    

    注意:此切面的优先级要高于事务的优先级

    注解类

    注解可作用于方法、接口、类上

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CustomDataSource {
    
      String value();
    
      String D01 = "dataSource01";
    
      String D02 = "dataSource02";
    
    }
    

    切面类

    public class DataSourceAspect {
      /**
       * 拦截目标方法,通过注解来获取数据源名称key值,设置到ThreadLocal中
       */
      public void intercept(JoinPoint point) throws Exception {
        Class<?> target = point.getTarget().getClass();
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 使用目标注解类型,如果没有,则使用其接口指定类型
        for (Class<?> clazz : target.getInterfaces()) {
          resolveDataSource(clazz, signature.getMethod());
        }
        resolveDataSource(target, signature.getMethod());
      }
    
      /**
       * 获取目标对象方法注解和类型注解中的注解
       */
      private void resolveDataSource(Class<?> clazz, Method method) {
        try {
          Class<?>[] types = method.getParameterTypes();
          // 默认使用类型注解
          if (clazz.isAnnotationPresent(CustomDataSource.class)) {
            CustomDataSource cds = clazz.getAnnotation(CustomDataSource.class);
            BindingDataSourceKey.setDataSourceToken(cds.value());
          }
          // 方法注解覆盖,以方法注解为最后值
          Method m = clazz.getMethod(method.getName(), types);
          if (m != null && m.isAnnotationPresent(CustomDataSource.class)) {
            CustomDataSource cds = m.getAnnotation(CustomDataSource.class);
            BindingDataSourceKey.setDataSourceToken(cds.value());
          }
        } catch (Exception e) {
          log.error(clazz + ":" + e.getMessage());
        }
      }
    
    }
    

    ThreadLocal 类

    public class BindingDataSourceKey {
        private static final ThreadLocal<String> THREAD_LOCAL_TOKEN = new ThreadLocal<String>();
        
        public static String getDataSourceToken() {
            return THREAD_LOCAL_TOKEN.get();
        }
        
        public static void setDataSourceToken(String dataSource) {
            THREAD_LOCAL_TOKEN.set(dataSource);
        }
        
        public static void clearDataSourceToken() {
            THREAD_LOCAL_TOKEN.remove();
        }
    }
    

    获取数据源对应key的类

    public class DynamicDataSource extends AbstractRoutingDataSource {
      @Override
      protected Object determineCurrentLookupKey() {
        String dataSource = BindingDataSourceKey.getDataSourceToken();
        // 获取完后解除绑定,防止其他的service方法调用该数据源
        BindingDataSourceKey.clearDataSourceToken();
        return dataSource;
      }
    }
    

    之后需要在注解的类上、方法或是接口上加上注解@CustomDataSource(CustomDataSource.D02),就可以通过切入点表达式进行获取注解情况,从而控制数据源

    相关文章

      网友评论

          本文标题:通过注解配置多数据源切换

          本文链接:https://www.haomeiwen.com/subject/kejemttx.html