1. 程式人生 > >AbstractRoutingDataSource+AOP+JNDI實現spring動態數據源

AbstractRoutingDataSource+AOP+JNDI實現spring動態數據源

如果 mes first ray 2個 xxxxx out str new

參考: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是具體的文件名稱 .* 表示所有的方法 ()表示參數

AbstractRoutingDataSource+AOP+JNDI實現spring動態數據源