1. 程式人生 > >spring多資料來源配置+aop註解方式屬性注入

spring多資料來源配置+aop註解方式屬性注入

本文將介紹spring中多個數據源的配置,同時使用註解的方式切換選擇資料來源。

spring的其他配置不再細說,只說資料來源的相關配置。

參考多篇博文實踐整理,不在一一查找出處,如有侵權請及時聯絡

====================================

1.修改spring配置

1.1加入資料來源

......
    <!-- 配置資料來源 使用的是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> ......
1.2用統一的類處理,切換資料來源,指定如果不設定,預設使用第一個資料來源
    <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
我用的是1.7版本的jdk所以在pom.xml中需配置
<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即可