标签:初始 ati rac 容器 extends strong sources ast dde
在同一个项目中需要使用多个数据源,这就需要根据不同的场景进行切换数据源,spring给我们提供一种很方便的方式,那就是使用 AbstractRoutingDataSource 进行切换数据源。
首先来看 AbstractRoutingDataSource 这个类,下面是这个类源码。
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
// 需要存储的所有数据源,需要在 bean 生成后进行注入,key 为获得数据源名称,value 为数据源
private Map<Object, Object> targetDataSources;
private Object defaultTargetDataSource; // 初始设计的默认数据源
// 如果在查找数据源时,找不到相应的数据源时是否使用默认数据源
private boolean lenientFallback = true;
// 通过 JNDI 方式的来获取一个数据源
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
// 将 targetDataSources 通过转化得到的数据源的集合。(如果不使用 JNDI 方式,它和 targetDataSources 是一样的)
private Map<Object, DataSource> resolvedDataSources;
// 转化后的默认数据源,如果在数据源的 key 为 null 时会使用,默认数据源,当然还有其他情况,下面会介绍
private DataSource resolvedDefaultDataSource;
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
}
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
this.defaultTargetDataSource = defaultTargetDataSource;
}
public void setLenientFallback(boolean lenientFallback) {
this.lenientFallback = lenientFallback;
}
public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
}
/**
* 因为该类实现了 InitializingBean,所以会在 bean 生成之后(@Bean 注解执行之后,或者 xml 解析完之后)执行该方法
*/
@Override
public void afterPropertiesSet() {
if (this.targetDataSources == null) { // 必须在 Bean 生成后注入数据源
throw new IllegalArgumentException("Property ‘targetDataSources‘ is required");
}
this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
Object lookupKey = resolveSpecifiedLookupKey(entry.getKey()); // 对数据源名称进行重定义
DataSource dataSource = resolveSpecifiedDataSource(entry.getValue()); // 获取数据源
this.resolvedDataSources.put(lookupKey, dataSource);
}
if (this.defaultTargetDataSource != null) { // 设置默认数据源
this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
}
}
/**
* 变换 lookupKey,由子类实现, 默认是 targetDataSources 的原始 key
*/
protected Object resolveSpecifiedLookupKey(Object lookupKey) {
return lookupKey;
}
/**
* 获取数据,直接获取,或者是通过 JNDI 的方法进行获取
*/
protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
if (dataSource instanceof DataSource) { // targetDataSources 中直接存储的就是数据源
return (DataSource) dataSource;
}
else if (dataSource instanceof String) { // 通过 JNDI 的方法获取数据源
return this.dataSourceLookup.getDataSource((String) dataSource);
}
else {
throw new IllegalArgumentException(
"Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
}
}
/**
* 获取数据源的数据库连接
*/
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
/**
* 通过用户名和密码的方式获取数据库连接
*/
@Override
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return (T) this;
}
return determineTargetDataSource().unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
}
/**
* 通过 lookupKey 来获取数据源
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey(); // 获取需要数据源的 lookupKey
// 从已经转化过的数据源查找相应的数据源
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
// 如果没有找到数据源,或者可以使用默认数据源,或者 lookupKey 为 null,则使用默认数据源
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
/**
* 由子类实现,返回需要获取的数据源的 lookupKey
*/
protected abstract Object determineCurrentLookupKey();
}
如果要实现多数据源的方式就必须要继承类,然后实现其抽象方法,下面给出一种我的实现方式:
多数据源的的实现:
public class MultipleDataSource extends AbstractRoutingDataSource {
// 通过 ThreadLocal 的方式来为每个线程一存储个不同的数据源名称
private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<>();
/**
* 设置数据源,可以考虑使用注解 aop 的方式,在每个方法执行进行拦截,然后将数据源名称(lookupKey) 设置进行去
*/
public static void setDataSourceKey(String dataSource) {
dataSourceKey.set(dataSource);
}
/**
* 实现父类的获取数据名称的方法
*/
@Override
protected Object determineCurrentLookupKey() {
String dsName = dataSourceKey.get();
dataSourceKey.remove(); // 注意要删除,否则可能内存泄漏
return dsName;
}
}
多数据源的注入 spring 容器:
@Bean("master")
public DataSource masterDataSource(){
return new C3p0SingleDataSource();
}
@Bean("slaver")
public DataSource slaverDataSource(){
return new BladeDataSource();
}
@Bean
public DataSource dataSource(@Qualifier("master") DataSource master, @Qualifier("slaver") DataSource slaver){
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", master);
targetDataSources.put("slaver", slaver);
MultipleDataSource multipleDataSource = new MultipleDataSource();
multipleDataSource.setTargetDataSources(targetDataSources); // 注入
multipleDataSource.setDefaultTargetDataSource(master); // 设置默认数据源
return multipleDataSource;
}
注意上面的 master 和 slaver 数据源都是随便 new 的,具体使用时还需要自己设置数据库的相应属性。
再考虑写一个多数据源的注解和 aop,可以加在方法和类上面,可以进行多数据源的使用:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "";
}
@Aspect
@Component
public class DataSourceAspect {
public DataSourceAspect() {
}
@Around("@within(dataSource) || @annotation(dataSource)")
public Object around(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable {
if (null == dataSource) {
dataSource = (DataSource)extractClassLevelAnnotation(joinPoint, DataSource.class);
}
MultipleDataSource.setDataSourceKey(dataSource.value());
return joinPoint.proceed();
}
public static Annotation extractClassLevelAnnotation(JoinPoint joinPoint, Class clazz) {
for(Annotation annotation : joinPoint.getTarget().getClass().getAnnotations()) {
if (annotation.annotationType() == clazz) {
return annotation;
}
}
throw new RuntimeException("类:" + joinPoint.getTarget().getClass().getSimpleName() + "没有找到" + clazz.getName() + "注解");
}
}
DataSource 注解可以类上面,表示类中的所有方法都使用该数据源,也可以加在方法上面,如果都加了,则会使用方法上面的那个。
@DataSource("slave")
public interface UserDao {
@DataSource("master")
int insert(UserPO userPO);
}
标签:初始 ati rac 容器 extends strong sources ast dde
原文地址:https://www.cnblogs.com/dwtfukgv/p/14848407.html