AbstractRoutingDataSource+AOP+JNDI实现spring动态数据源
参考:https://www.cnblogs.com/wyb628/p/7240061.html
- 背景:
系统已有数据源1(主要数据源),数据源2(只有一个目录的xml使用该数据源),由于这2个数据源分别扫描不同的包,相互不打扰,所以一直用的好好的。
直到,需要新增一个数据源3,跟数据源2用法一模一样的,但是需要在程序中具体用到的时候才能决定具体使用哪一个。所以,基于此,针对数据源2和3实现了动态数据源。
- 思路:
- sessionFactory的dataSource属性设置成能从代码中动态读取,继承类:org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
- 使用ThreadLocal为每一个线程单独设置数据源的key,该key可以匹配到配置文件中的jndi。
- 通过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是具体的文件名称 .* 表示所有的方法 ()表示参数