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 versionspring versionaspectjrt version and aspectjweaver version
1.63.0 +aspectjrt-1.6.2  and aspectjweaver-1.6.2
1.73.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即可






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值