实现功能
在dao层的不同接口可以调用不同的数据源,
更进一步,相同接口中的不同方法也可以调用不同的数据源
设计思想
要实现数据源动态切换,
首先要借助spring的数据源路由器AbstractRoutingDataSource的数据源路由功能。
看它的源码有这样一个方法
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
protected abstract Object determineCurrentLookupKey();
其中的注释我们关心的就是前两句:
第一,这个方法是用来查找key的。
第二,用这个方法查到的事务上下文(可以理解为数据库连接)要是与线程绑定的。
也就是说我们继承AbstractRoutingDataSource,
重写determineCurrentLookupKey方法,
在我们的实现中返回一个与当前线程绑定的数据库key就可以了。(而这个key就是在targetDataSources属性中的key,也是配置下面配置文件中< map >中的key)
现在我们知道了如何设置数据源,那该在什么时候去设置呢?
因为要在调用不同的dao方法时使用不同的数据源,那就要在这些方法执行之前对数据源进行设置。
这里自然就想到了切面,把动态设置key的逻辑写在before切面中。
最后,要把方法和对应数据库的key做对应,使其可以在切面中进行配置。
参考spring的种种实现,想要把方法和配置进行关联时往往都会用到注解,所以我们可以自定义一个注解用来在dao方法上面配置数据源的key,然后在before切面中就可以使用反射读取到配置值了。
一 java代码
1.自定义注解实现具体方法和数据源别名对应。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
2.自定义Constant保存当前线程的数据源的别名。
public class DataSourceConstant {
public static final ThreadLocal<String> dataSourceKey = new ThreadLocal<String>();
}
2.使用Aspect在方法执行之前动态设置数据源别名。
@Component
@Aspect
public class DataSourceAspect {
//扫描dao类所在包
@Before("execution(* com.fxp.dao..*.*(..))")
public void before(JoinPoint point) throws Exception {
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
DataSourceConstant.dataSourceKey.set(data.value());
}
}
}
3.继承AbstractRoutingDataSource重写determineCurrentLookupKey方法,取DataSourceConstant中保存的数据源别名。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicRoutingDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DataSourceConstant.dataSourceKey.get();
}
}
二 xml配置
1.切面注解支持
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.配置数据源路由
<bean id="multipleDataSource" class="com.fxp.dynamicdatasource.DynamicRoutingDataSource">
<!-- 默认数据源 -->
<property name="defaultTargetDataSource" ref="数据源1"/>
<!-- 数据源路由 -->
<property name="targetDataSources">
<map>
<entry key="datasource1" value-ref="数据源1"/>
<entry key="datasource2" value-ref="数据源2"/>
</map>
</property>
</bean>
3.数据源及其他配置略。
三 使用
mybatis示例。
public interface UserMapper {
@DataSource("datasource1")
List<User> selectAll();
@DataSource("datasource2")
void insertOne(User user);
}