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

    参考:https://www.cnblogs.com/wyb628/p/7240061.html

    • 背景:

    系统已有数据源1(主要数据源),数据源2(只有一个目录的xml使用该数据源),由于这2个数据源分别扫描不同的包,相互不打扰,所以一直用的好好的。

    直到,需要新增一个数据源3,跟数据源2用法一模一样的,但是需要在程序中具体用到的时候才能决定具体使用哪一个。所以,基于此,针对数据源2和3实现了动态数据源。

    • 思路:
    1. sessionFactory的dataSource属性设置成能从代码中动态读取,继承类:org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
    2. 使用ThreadLocal为每一个线程单独设置数据源的key,该key可以匹配到配置文件中的jndi。
    3. 通过aop进行动态的设置key,使用完毕remove,防止出现内存泄露。
    • 代码如下:

    spring-mybatis.xml配置数据源1和动态数据源2/3如下:<?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:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/jee 
        http://www.springframework.org/schema/jee/spring-jee-4.3.xsd">
    
        <!-- data source 1-->
        <jee:jndi-lookup id="dataSource" lookup-on-startup="false"
            proxy-interface="javax.sql.DataSource" jndi-name="${first.jndi.database}" />
            
        <!--  data source 2-->
        <jee:jndi-lookup id="secondDataSource"
            lookup-on-startup="false" proxy-interface="javax.sql.DataSource"
            jndi-name="${second.jndi.database}" />
        <!-- data source 3-->
        <jee:jndi-lookup id="thirdDataSource"
        lookup-on-startup="false" proxy-interface="javax.sql.DataSource"
        jndi-name="${third.jndi.database}"/>
            
        <bean id="PaginationInterceptor" class="XXXX.mybatis.plugins.PaginationInterceptor"/>
        
        <!-- 数据源1的配置 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
            <property name="mapperLocations">
            <array>
                <value>classpath:XXXXX/database/*/impl/*.xml</value>
                <value>classpath*:XXXX/client/dao/**/*Mapper.xml</value>
                <value>classpath*:XXXXX/restructure/**/*Mapper.xml</value>
            </array>
            </property>
            <property name="plugins">
                <array>
                    <ref bean="PaginationInterceptor" />
                    <ref bean="myInterceptor"/>
                </array>
            </property>
        </bean>
    
        <bean id="myInterceptor" class="XXXXX.mybatis.MyInterceptor"></bean>
    
    
    
        <!--配置数据源1的扫描路径-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="XXXX.data.database" />
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        </bean>
    
        <!-- 配置事务管理器 -->
        <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- 注解方式配置事务 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
    
        <!-- 动态数据源的配置-->
        <bean id="myDynamicDataSource" class="XXXX.DynamicDataSource">
        <property name="targetDataSources">
        <map key-type="java.lang.String">
        <!--注意这里key跟代码中对应即可 value要跟配置的jndi对应-->
        <entry value-ref="secondDataSource" key="mySecond"/>
        <entry value-ref="thirdDataSource" key="myThree"/>
        </map>
        </property>
        <!--配置默认的--->
        <property name="defaultTargetDataSource" ref="secondDataSource"/>
        </bean>
    
        <bean id="mySqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="myDynamicDataSource" />
            <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
            <property name="mapperLocations"
                value="classpath:YYYYY/*/impl/*.xml" />
        </bean>
    
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="YYYYYXXXX.YYYdatabase" />
            <property name="sqlSessionFactoryBeanName" value="mySqlSessionFactory" />
        </bean>
    
        <!-- 配置事务管理器 -->
        <bean id="transactionManager_my"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="myDynamicDataSource" /> 
    </bean>
    </beans>

    其中:<bean id="myDynamicDataSource" class="XXXX.DynamicDataSource">

    DynamicDataSource.java内容如下:

    package XXXX;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceContextHolder.getSourceType();
        }
    }
    DataSourceContextHolder.java内容如下:
    
    

    package XXXX;

    
    

    public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextDynamicSourceHolder = new ThreadLocal<String>();

    
    

    public static void setDataSourceType(String sourceType) {
    contextDynamicSourceHolder.set(sourceType);
    }

    
    

    public static String getSourceType() {
    return contextDynamicSourceHolder.get();
    }

    
    

    public static void clearSourceType() {
    contextDynamicSourceHolder.remove();
    }
    }

     

    使用AOP对需要使用动态数据源的地方进行设置值,aop如下:

    package XXXX;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class contextDynamicSourceHolder {
        private   final Logger LOG=LoggerFactory.getLogger(contextDynamicSourceHolder.class);
        
        //切点设置为需要使用动态数据源的地方
        @Pointcut("execution(* XXXX.ZZZZMapper.*(..))")
        public void pointCut() {
    
        }
    
        @Before("pointCut()")
        public void before(JoinPoint jp) {
            Object[] args=jp.getArgs();
            Signature signature = jp.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            String[] argNames = methodSignature.getParameterNames();
            //省略判断条件,根据判断条件设置数据源的key
            DataSourceContextHolder.setDataSourceType(XXX);
        }
    
        @AfterReturning("pointCut()")
        public void afterReturnning() {
            DataSourceContextHolder.clearSourceType();
        }
    
        @AfterThrowing("pointCut()")
        public void afterThrowing() {
            DataSourceContextHolder.clearSourceType();
        }
    }
     如果aop没有生效,
    1.检查一下spring.xml配置文件中是否有:<aop:aspectj-autoproxy/>
    2.检查切点表达式是否正确
    比如:execution(* XXX.UUUMapper.*(..))

         XXX是路径  UUUMapper是具体的文件名称  .* 表示所有的方法 ()表示参数

  • 相关阅读:
    Effective Java 第三版——72. 赞成使用标准异常
    Effective Java 第三版——71. 避免不必要地使用检查异常
    Effective Java 第三版——70. 对可恢复条件使用检查异常,对编程错误使用运行时异常
    Effective Java 第三版——69. 仅在发生异常的条件下使用异常
    Effective Java 第三版——68. 遵守普遍接受的命名约定
    Effective Java 第三版——67. 明智谨慎地进行优化
    Effective Java 第三版——66. 明智谨慎地使用本地方法
    Effective Java 第三版——65. 接口优于反射
    Effective Java 第三版——64. 通过对象的接口引用对象
    Effective Java 第三版——63. 注意字符串连接的性能
  • 原文地址:https://www.cnblogs.com/LittleSix/p/10816497.html
Copyright © 2011-2022 走看看