spring多資料來源配置+aop註解方式屬性注入
阿新 • • 發佈:2019-01-09
本文將介紹spring中多個數據源的配置,同時使用註解的方式切換選擇資料來源。
spring的其他配置不再細說,只說資料來源的相關配置。
參考多篇博文實踐整理,不在一一查找出處,如有侵權請及時聯絡
====================================
1.修改spring配置
1.1加入資料來源
1.2用統一的類處理,切換資料來源,指定如果不設定,預設使用第一個資料來源...... <!-- 配置資料來源 使用的是Druid資料來源 --> <bean id="dataSourceOne" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <!-- 配置監控統計攔截的filters,去掉後監控介面sql無法統計 --> <property name="filters" value="stat" /> </bean> <bean id="dataSourceTwo
" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver2}" /> <property name="url" value="${jdbc.url2}"></property> <property name="username" value="${jdbc.username2}"></property> <property name="password" value="${jdbc.password2}"></property> <!-- 配置監控統計攔截的filters,去掉後監控介面sql無法統計 --> <property name="filters" value="stat" /> </bean> ......
<bean id="dynamicDataSource" class="com.best.core.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSourceOne" key="dataSourceOne"></entry>
<entry value-ref="dataSourceTwo" key="dataSourceTwo"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceOne">
</property>
</bean>
1.3在sqlsession中引用
<!-- myBatis檔案 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="mapperLocations" value="classpath:sqlmap/*.xml" />
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<!-- 這裡的幾個配置主要演示如何使用,如果不理解,一定要去掉下面的配置 -->
<property name="properties">
<value>
helperDialect=mysql
reasonable=true
supportMethodsArguments=true
params=count=countSql
</value>
</property>
</bean>
</array>
</property>
</bean>
2.資料來源控制類的編寫
2.1DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSource();
}
}
2.2DynamicDataSourceHolder.java
public class DynamicDataSourceHolder {
/**
* 注意:資料來源標識儲存線上程變數中,避免多執行緒操作資料來源時互相干擾
*/
private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
public static String getDataSource() {
return THREAD_DATA_SOURCE.get();
}
public static void setDataSource(String dataSource) {
THREAD_DATA_SOURCE.set(dataSource);
}
public static void clearDataSource() {
THREAD_DATA_SOURCE.remove();
}
}
3.呼叫在相關的dao層加上資料來源的切換即可,如:
DynamicDataSourceHolder.setDataSource("dataSourceTwo");
List<User> list = userMapper.selectAll();
4.使用spring的aop實現註解選擇資料來源
4.1定義註解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value();
}
4.2配置aop(基於xml配置檔案的方式) <bean id="dataSourceAspect" class="com.best.core.DataSourceAspect" />
<aop:config>
<aop:aspect ref="dataSourceAspect">
<!-- 攔截所有mapper方法 -->
<aop:pointcut id="dataSourcePointcut" expression="execution(* com.best.mapper.*.*(..))"/>
<aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
</aop:aspect>
</aop:config>
在spring中使用aop除了匯入相關jar包,別忘了一下程式碼
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd ">
4.3編寫aop的處理類
public class DataSourceAspect {
/**
* 攔截目標方法,獲取由@DataSource指定的資料來源標識,設定到執行緒儲存中以便切換資料來源
*
* @param point
* @throws Exception
*/
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());
}
/**
* 提取目標物件方法註解和型別註解中的資料來源標識
*
* @param clazz
* @param method
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
DataSource source =null;
// 預設使用型別註解
if (clazz.isAnnotationPresent(DataSource.class)) {
source = clazz.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
// 方法註解可以覆蓋型別註解
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
source = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
} catch (Exception e) {
System.out.println(clazz + ":" + e.getMessage());
}
}
}
4.4註解的使用,可以在類或者方法上宣告,取決於DataSourceAspect類的編寫
@DataSource("dataSourceTwo")
public interface UserMapper extends BaseMapper<User, Integer>{
User selectByMobile(String mobile);
int updateByMobileSelective(User user);
List<User> selectStudents();
}
以上是基於xml檔案的aop配置從4.2開始,也可以使用註解的方式配置aop,但是存在一些問題,在這裡提一下,不推薦
4.2配置aop
在xml配置檔案中加入
<!-- 啟用自動代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
4.3編寫aop處理類
@Component
@Aspect
//@EnableAspectJAutoProxy(proxyTargetClass = true) //若已經在xml中配置此處可以省略
public class DataSourceAspect {
//配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
@Pointcut("execution(* com.best.mapper.*.*(..))")
public void aspect(){}
/**
* 攔截目標方法,獲取由@DataSource指定的資料來源標識,設定到執行緒儲存中以便切換資料來源
*
* @param point
* @throws Exception
*/
@Before("aspect()")
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());
}
/**
* 提取目標物件方法註解和型別註解中的資料來源標識
*
* @param clazz
* @param method
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
DataSource source =null;
// 預設使用型別註解
if (clazz.isAnnotationPresent(DataSource.class)) {
source = clazz.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
// 方法註解可以覆蓋型別註解
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
source = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
} catch (Exception e) {
System.out.println(clazz + ":" + e.getMessage());
}
}
}
使用方法不變
說說這種方式碰到的問題及解決方式:
1. 0 can't find referenced pointcut
這是開發環境jdk版本導致的,見下表
jdk version | spring version | aspectjrt version and aspectjweaver version |
1.6 | 3.0 + | aspectjrt-1.6.2 and aspectjweaver-1.6.2 |
1.7 | 3.0 + | aspectjrt-1.7.3 and aspectjweaver-1.7.3 |
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.3</version>
</dependency>
2.Cannot subclass final class class com.sun.proxy.$Proxy16
從Spring3.2以後,spring框架本身不在需要cglib這個jar包了,因為cjlib.jar已經被spring專案的jar包整合進去。為了防止專案中其他對cglib版本依賴不一樣的衝突。
所以切換spring版本為4.2.5.RELEASE即可