概述:
? ?核心是继承AbstractRoutingDataSource,需要实现determineCurrentLookupKey()拿到线程上下文targetDataSources目标数据源下的k对应的DataSource
defaultTargetDataSource:是默认数据源类型DataSourece?
targetDataSources是<k,v> k,数据源id,v是DataSourece?
根据放方法determineCurrentLookupKey(),该方法从线程DynamicDataSourceContextHolder上下文获取数据源ID
当前线程获取获取注解下的
//设置默认数据源
super.setDefaultTargetDataSource(defaultTargetDataSource);
//设置数据源集合
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
第一:定义一个切换数据源注解
import java.lang.annotation.*;
/**
* 动态数据源注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSupport {
/**
* 数据源ID
*/
String value();
}
第二:定义一个切换数据源的@Aspect注解类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* @author 我来秋风扫落叶
* @date 20212/4/15
* @description 多数据源处理
*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.plat.net.dymdatasource.DataSupport)" + "|| @within(com.plat.net.dymdatasource.DataSupport)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
DataSupport dataSupport = getDataSource(point);
if (null != dataSupport) {
if (logger.isDebugEnabled()){
logger.debug("切换数据源为:{}",dataSupport.value());
}
DynamicDataSourceContextHolder.setDataSourceType(dataSupport.value());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
/**
* 获取需要切换的数据源
*/
public DataSupport getDataSource(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSupport dataSupport = AnnotationUtils.findAnnotation(signature.getMethod(), DataSupport.class);
if (Objects.nonNull(dataSupport)) {
return dataSupport;
}
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSupport.class);
}
}
第三:定义一个数据源加载配置类
import com.alibaba.druid.pool.DruidDataSource;
import com.plat.net.util.SpringUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfig {
@Resource
SpringUtil springUtil;
/**
* 普通数据源1---加载时候加载进去
*/
@Bean(name = "ds1")
public DataSource ds1() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf8&useSSL=false");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return druidDataSource;
}
/**
* 普通数据源2---加载时候加载进去
*/
@Bean(name = "ds2")
public DataSource ds2() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf8&useSSL=false");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return druidDataSource;
}
/**
* 普通数据源3---加载时候加载进去
*/
@Bean(name = "ds3")
public DataSource ds3() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test3?useUnicode=true&characterEncoding=utf8&useSSL=false");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return druidDataSource;
}
/**
* 动态切换数据源路由
*/
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSourceRoute dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
//targetDataSources里面承载的是备选数据源,key是数据源名称,value是数据源DataSource
setDataSource(targetDataSources, "ds1", "ds1");
setDataSource(targetDataSources, "ds2", "ds2");
setDataSource(targetDataSources, "ds3", "ds3");
return new DynamicDataSourceRoute(ds1(), targetDataSources);
}
/**
* 设置数据源
*
* @param targetDataSources 备选数据源集合
* @param sourceName 数据源名称
* @param beanName bean名称
*/
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
try {
DataSource dataSource = (DataSource) springUtil.getBean(beanName);
targetDataSources.put(sourceName, dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
}
第四:定义一个线程ThreadLocal 这个是每个线程主要作用就是获取当前自己访问的数据源,ThreadLocal记得用完必须清掉,不然会累计造成内存溢出
作用:获取当前线程的数据源ID,供给:DynamicDataSourceRoute调用,这个是spring数据源路由选择
/**
* 动态数据源上下文
*/
public class DynamicDataSourceContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> local = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType) {
local.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType() {
return local.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType() {
local.remove();
}
}
第五:spring 数据源切换 DynamicDataSourceRoute
该类是继承抽象类:AbstractRoutingDataSource,也是核心选择数据源的抽象接口
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* spring 实现多数据源选择继承AbstractRoutingDataSource
* spring 加载bean的时候将该数据源加载进去
**/
public class DynamicDataSourceRoute extends AbstractRoutingDataSource {
public DynamicDataSourceRoute(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
//设置默认数据源
super.setDefaultTargetDataSource(defaultTargetDataSource);
//设置数据源集合
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
/**
* 根据当前线程选择数据源---当前线程选择的数据源为当前线程的数据源
* 对应:DataSupport
**/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
|