根据header中的租户/商户标识或者指定的参数(租户/商户标识)动态切换数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> dataSourceKey = ThreadLocal.withInitial(() -> "defaultDataSource");
public static Map<Object, Object> dataSourcesMap = new ConcurrentHashMap<>(10);
static {
dataSourcesMap.put("defaultDataSource", SpringUtils.getBean("defaultDataSource"));
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSource.dataSourceKey.get();
}
public static void setDataSource(String dataSource) {
DynamicDataSource.dataSourceKey.set(dataSource);
DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringUtils.getBean("dataSource");
dynamicDataSource.afterPropertiesSet();
}
public static String getDataSource() {
return DynamicDataSource.dataSourceKey.get();
}
public static void clear() {
DynamicDataSource.dataSourceKey.remove();
}
public static void setDataSourceKey(String dataSource) {
DynamicDataSource.dataSourceKey.set(dataSource);
}
public static void putIfAbsent(String url, DruidDataSource druidDataSource) {
DynamicDataSource.dataSourceKey.set(url);
Object oldDruidDataSource = DynamicDataSource.dataSourcesMap.putIfAbsent(url, druidDataSource);
if (druidDataSource != oldDruidDataSource) {
addDruidDataSource();
}
}
private static synchronized void addDruidDataSource() {
DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringUtils.getBean("dataSource");
dynamicDataSource.afterPropertiesSet();
}
}
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource defaultDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
@DependsOn({"springUtils", "defaultDataSource"})
public DynamicDataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(DynamicDataSource.dataSourcesMap);
return dynamicDataSource;
}
}
@Component
@Aspect
@Order(0)//配置Spring注解事务时,在事务之前切换数据源
@Slf4j
public class DynamicDataSourceAop {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IOperateService iOperateService;
@Pointcut("@annotation(com.bp.annotation.SwitchDataBase)")
public void pointCut() {
System.out.println("找到切面");
}
/**
* 执行方法前更换数据源
*
* @param joinPoint
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
SwitchDataBase annotation = method.getAnnotation(SwitchDataBase.class);
if (annotation == null) {
//获取类上的注解
annotation = joinPoint.getTarget().getClass().getAnnotation(SwitchDataBase.class);
if (annotation == null) {
//获取接口上的注解
for (Class<?> cls : joinPoint.getClass().getInterfaces()) {
annotation = cls.getAnnotation(SwitchDataBase.class);
if (annotation != null) {
break;
}
}
}
}
Long bpId = 0L;
Object jdbcUrl = "";
String jdbcUrlKey = "";
if (annotation != null) {
DruidDataSource druidDataSource = new DruidDataSource();
int index = annotation.paramIndex();
Object[] args = joinPoint.getArgs();
if (CutType.token.equals(annotation.value())) {
String token = args[index].toString();
bpId = JWTUtil.getBpId(token);
if (bpId == null || bpId == 0L) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
bpId = JWTUtil.getBpId(request.getHeader("token"));
}
} else if (CutType.bpId.equals(annotation.value())) {
bpId = Long.parseLong(args[index].toString());
}
if (bpId == null || bpId == 0L) {
throw new RuntimeException("系统繁忙,请稍后重试");
}
ValueOperations ops = redisTemplate.opsForValue();
jdbcUrlKey = "bop.datasource.aop.jdbcUrl" + bpId;
jdbcUrl = ops.get(jdbcUrlKey);//bop.datasource.aop.jdbcUrl+bpId -> dataSourceUrl
if (null != jdbcUrl && !StringUtil.isBlank(jdbcUrl.toString()) && null != DynamicDataSource.dataSourcesMap.get(jdbcUrl) && null != DynamicDataSource.getDataSource()) {
DynamicDataSource.setDataSourceKey(jdbcUrl.toString());
} else {
Map<String, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("bpId", bpId);
RequestBean param = new RequestBean();
param.setData(dataSourceMap);
//todo 根据租户/商户查询数据源
ResponseBean responseBean = iOperateService.getDateSourceByBpId(param);
if (responseBean != null && ResponseConstant.SUCCESS.getCode().equals(responseBean.getCode())) {
JSONObject dateSource = JSONObject.parseObject(JSONObject.toJSONString(responseBean.getData()));
if (dateSource != null && !StringUtil.isBlank(dateSource.getString("url"))) {
if (null != DynamicDataSource.dataSourcesMap.get(dateSource.getString("url")) && null != DynamicDataSource.getDataSource()) {
DynamicDataSource.setDataSourceKey(jdbcUrl.toString());
ops.set(jdbcUrlKey, dateSource.getString("url"));
redisTemplate.expire(jdbcUrlKey, 5, TimeUnit.HOURS);
} else {
druidDataSource.setDriverClassName(dateSource.getString("driver_class_name"));
druidDataSource.setUrl(dateSource.getString("url"));
druidDataSource.setUsername(dateSource.getString("user_name"));
druidDataSource.setPassword(dateSource.getString("pass_word"));
druidDataSource.setInitialSize(1);//#初始连接数。
druidDataSource.setMinIdle(1);//#最小连接数 设为0表示无限制。
druidDataSource.setMaxActive(50);//#最大连接数 设为0表示无限制。
druidDataSource.setMaxWait(60000);//超时等待时
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(100);
// druidDataSource.setLogAbandoned(true);
// druidDataSource.setTestOnBorrow(true);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(150000);//#unit is millisecond,间隔检查150s
druidDataSource.setMinEvictableIdleTimeMillis(150000);//#unit is millisecond,最小空闲150s
druidDataSource.setMaxEvictableIdleTimeMillis(600000);//#unit is millisecond,最大空闲600s
druidDataSource.setKeepAlive(true);
druidDataSource.setValidationQuery("SELECT 1 FROM DUAL");
ops.set(jdbcUrlKey, dateSource.getString("url"));
redisTemplate.expire(jdbcUrlKey, 5, TimeUnit.HOURS);
DynamicDataSource.setDataSourceKey(jdbcUrl.toString());
DynamicDataSource.putIfAbsent(dateSource.getString("url"), druidDataSource);
}
} else {
throw new RuntimeException("系统繁忙,请稍后重试");
}
} else {
throw new RuntimeException("系统繁忙,请稍后重试");
}
}
}
Object[] args = joinPoint.getArgs();
return joinPoint.proceed(args);
}
}
@Component
public class SpringUtils implements ApplicationContextAware {
@Autowired
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtils.applicationContext == null) {
SpringUtils.applicationContext = applicationContext;
}
}
public static <T> T getBean(Class<T> clazz) {
return SpringUtils.applicationContext.getBean(clazz);
}
public static Object getBean(String name) {
return SpringUtils.applicationContext.getBean(name);
}
public static String getProperty(String key) {
return SpringUtils.applicationContext.getEnvironment().getProperty(key);
}
}