public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSouce();
}
}
/**
* 为数据源分配线程
* @author xw
*
*/
public class DynamicDataSourceHolder {
public static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void putDataSource(String name) {
holder.set(name);
}
public static String getDataSouce() {
return holder.get();
}
}
<bean name="masterdataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 数据库驱动类名。 -->
<property name="driverClassName" value="${jdbc1.driverClass}" />
<!-- 连接数据库时使用的URL。 -->
<property name="url" value="${jdbc1.jdbcUrl}" />
<!-- 连接数据库时使用的用户名。 -->
<property name="username" value="${jdbc1.user}" />
<!-- 连接数据库时使用的用户密码。 -->
<property name="password" value="${jdbc1.password}" />
</bean>
<bean name="slavedataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 数据库驱动类名。 -->
<property name="driverClassName" value="${jdbc2.driverClass}" />
<!-- 连接数据库时使用的URL。 -->
<property name="url" value="${jdbc2.jdbcUrl}" />
<!-- 连接数据库时使用的用户名。 -->
<property name="username" value="${jdbc2.user}" />
<!-- 连接数据库时使用的用户密码。 -->
<property name="password" value="${jdbc2.password}" />
</bean>
<bean id="dataSource" class="com.djzh.core.dataSource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterdataSource" />
<entry key="slave" value-ref="slavedataSource" />
</map>
</property>
</bean>
jdbc1.driverClass=com.mysql.jdbc.Driver
jdbc1.jdbcUrl=jdbc:mysql://192.168.26.1:3306/test1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc1.user=1
jdbc1.password=1
jdbc2.driverClass=com.mysql.jdbc.Driver
jdbc2.jdbcUrl=jdbc:mysql://192.168.26.1/test2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc2.user=1
jdbc2.password=1
/**
* Aop实现动态对数据源的参数赋值以获取不同数据源
* @author xw
*
*/
public class DataSourceAspect {
public void before(JoinPoint point)
{
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
//Class<?> classz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.putDataSource(data.value());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
<!-- 配置数据库注解aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="manyDataSourceAspect" class="com.djzh.core.dataSource.DataSourceAspect" />
<aop:config>
<aop:aspect id="c" ref="manyDataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.djzh.core.dao.*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
Jdk 动态代理。具体有如下四步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
GCLIB代理
cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。
原理区别:
jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
回归代码中,出问题的是有实现接口的,所以默认使用的是jdk动态代理,
但是TipsTransferServiceImpl和 context.getBean("TipsTransferService")是同级的,都算是实现了接口TipsTransferService,所以不能用来转换。而这里
可以使用cglib动态代理,因为它的实现方式不同于jdk动态代理。
在spring-aop.xml中可以配置强制使用cglib动态代理方式。以下链接中找到
TipsTransferService service= (TipsTransferServiceImpl) context.getBean("TipsTransferService");
TipsTransferService service= (TipsTransferService) context.getBean("TipsTransferService");