|
@@ -8,16 +8,10 @@ import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSour
|
|
|
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
|
|
import com.chelvc.framework.common.util.StringUtils;
|
|
|
import com.google.common.collect.Maps;
|
|
|
-import lombok.NonNull;
|
|
|
import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
import org.aspectj.lang.annotation.Around;
|
|
|
import org.aspectj.lang.annotation.Aspect;
|
|
|
import org.springframework.aop.framework.AopProxyUtils;
|
|
|
-import org.springframework.beans.BeansException;
|
|
|
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|
|
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
|
-import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
|
|
-import org.springframework.beans.factory.support.RootBeanDefinition;
|
|
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|
|
import org.springframework.core.Ordered;
|
|
|
import org.springframework.core.annotation.Order;
|
|
@@ -29,159 +23,77 @@ import org.springframework.util.CollectionUtils;
|
|
|
* @author Woody
|
|
|
* @date 2024/1/30
|
|
|
*/
|
|
|
+@Aspect
|
|
|
+@Order(Ordered.HIGHEST_PRECEDENCE)
|
|
|
@ConditionalOnClass(DynamicDataSourceContextHolder.class)
|
|
|
-public class DynamicDatasourceInterceptor implements BeanDefinitionRegistryPostProcessor {
|
|
|
- /**
|
|
|
- * 判断是否是兼容模式
|
|
|
- *
|
|
|
- * @return true/false
|
|
|
- */
|
|
|
- private boolean isCompatible() {
|
|
|
- try {
|
|
|
- // 尝试加载@DubboService注解
|
|
|
- this.getClass().getClassLoader().loadClass("org.apache.dubbo.config.annotation.DubboService");
|
|
|
- } catch (ClassNotFoundException e) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
|
|
- }
|
|
|
+public class DynamicDatasourceInterceptor {
|
|
|
+ private final Map<String, String> datasourceContext;
|
|
|
|
|
|
- @Override
|
|
|
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
|
|
- if (this.isCompatible()) {
|
|
|
- registry.registerBeanDefinition("compatibleDynamicDatasourceRouter",
|
|
|
- new RootBeanDefinition(CompatibleDynamicDatasourceRouter.class));
|
|
|
+ public DynamicDatasourceInterceptor(DynamicDataSourceProperties properties) {
|
|
|
+ // 初始化数据源标识集合
|
|
|
+ Set<String> keys = properties.getDatasource().keySet();
|
|
|
+ if (CollectionUtils.isEmpty(keys)) {
|
|
|
+ this.datasourceContext = Collections.emptyMap();
|
|
|
} else {
|
|
|
- registry.registerBeanDefinition("standardDynamicDatasourceRouter",
|
|
|
- new RootBeanDefinition(StandardDynamicDatasourceRouter.class));
|
|
|
+ this.datasourceContext = Maps.newHashMapWithExpectedSize(keys.size());
|
|
|
+ keys.forEach(value -> {
|
|
|
+ for (String key : StringUtils.splitActives(value)) {
|
|
|
+ this.datasourceContext.put(key, value);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 动态数据源路由器抽象实现
|
|
|
+ * 查找数据源标识
|
|
|
+ *
|
|
|
+ * @param point 方法拦截点
|
|
|
+ * @return 数据源标识
|
|
|
*/
|
|
|
- public static abstract class AbstractDynamicDatasourceRouter {
|
|
|
- private final Map<String, String> datasourceContext;
|
|
|
+ private String lookupDatasourceKey(ProceedingJoinPoint point) {
|
|
|
+ Object target = point.getTarget();
|
|
|
|
|
|
- public AbstractDynamicDatasourceRouter(@NonNull DynamicDataSourceProperties properties) {
|
|
|
- // 初始化数据源标识集合
|
|
|
- Set<String> keys = properties.getDatasource().keySet();
|
|
|
- if (CollectionUtils.isEmpty(keys)) {
|
|
|
- this.datasourceContext = Collections.emptyMap();
|
|
|
- } else {
|
|
|
- this.datasourceContext = Maps.newHashMapWithExpectedSize(keys.size());
|
|
|
- keys.forEach(value -> {
|
|
|
- for (String key : StringUtils.splitActives(value)) {
|
|
|
- this.datasourceContext.put(key, value);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ // 根据目标类对象查找数据源标识
|
|
|
+ String className = AopProxyUtils.ultimateTargetClass(target).getName();
|
|
|
+ Set<Map.Entry<String, String>> routes = this.datasourceContext.entrySet();
|
|
|
+ String key = routes.stream().filter(entry -> className.startsWith(entry.getKey())).findAny()
|
|
|
+ .map(Map.Entry::getValue).orElse(null);
|
|
|
+ if (StringUtils.notEmpty(key)) {
|
|
|
+ return key;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 查找数据源标识
|
|
|
- *
|
|
|
- * @param point 方法拦截点
|
|
|
- * @return 数据源标识
|
|
|
- */
|
|
|
- private String lookupDatasourceKey(ProceedingJoinPoint point) {
|
|
|
- Object target = point.getTarget();
|
|
|
-
|
|
|
- // 根据目标类对象查找数据源标识
|
|
|
- String className = AopProxyUtils.ultimateTargetClass(target).getName();
|
|
|
- Set<Map.Entry<String, String>> routes = this.datasourceContext.entrySet();
|
|
|
- String key = routes.stream().filter(entry -> className.startsWith(entry.getKey())).findAny()
|
|
|
- .map(Map.Entry::getValue).orElse(null);
|
|
|
- if (StringUtils.notEmpty(key)) {
|
|
|
- return key;
|
|
|
- }
|
|
|
-
|
|
|
- // 根据目标接口对象查找数据源标识
|
|
|
- Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
|
|
|
- if (interfaces.length > 0) {
|
|
|
- for (Class<?> clazz : interfaces) {
|
|
|
- String interfaceName = clazz.getName();
|
|
|
- String route = routes.stream().filter(entry -> interfaceName.startsWith(entry.getKey())).findAny()
|
|
|
- .map(Map.Entry::getValue).orElse(null);
|
|
|
- if (StringUtils.notEmpty(route)) {
|
|
|
- return route;
|
|
|
- }
|
|
|
+ // 根据目标接口对象查找数据源标识
|
|
|
+ Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
|
|
|
+ if (interfaces.length > 0) {
|
|
|
+ for (Class<?> clazz : interfaces) {
|
|
|
+ String interfaceName = clazz.getName();
|
|
|
+ String route = routes.stream().filter(entry -> interfaceName.startsWith(entry.getKey())).findAny()
|
|
|
+ .map(Map.Entry::getValue).orElse(null);
|
|
|
+ if (StringUtils.notEmpty(route)) {
|
|
|
+ return route;
|
|
|
}
|
|
|
}
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 数据源路由
|
|
|
- *
|
|
|
- * @param point 方法拦截点
|
|
|
- * @return 方法调用结果
|
|
|
- * @throws Throwable 方法调用异常
|
|
|
- */
|
|
|
- protected Object routing(ProceedingJoinPoint point) throws Throwable {
|
|
|
- DynamicDataSourceContextHolder.push(this.lookupDatasourceKey(point));
|
|
|
- try {
|
|
|
- return point.proceed(point.getArgs());
|
|
|
- } finally {
|
|
|
- DynamicDataSourceContextHolder.poll();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 动态数据源拦截器标准实现
|
|
|
- */
|
|
|
- @Aspect
|
|
|
- @Order(Ordered.HIGHEST_PRECEDENCE)
|
|
|
- public static class StandardDynamicDatasourceRouter extends AbstractDynamicDatasourceRouter {
|
|
|
- public StandardDynamicDatasourceRouter(DynamicDataSourceProperties properties) {
|
|
|
- super(properties);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 数据源路由
|
|
|
- *
|
|
|
- * @param point 方法拦截点
|
|
|
- * @return 方法调用结果
|
|
|
- * @throws Throwable 方法调用异常
|
|
|
- */
|
|
|
- @Around("@within(org.springframework.stereotype.Service) " +
|
|
|
- "|| @within(org.apache.ibatis.annotations.Mapper) " +
|
|
|
- "|| this(com.baomidou.mybatisplus.core.mapper.BaseMapper) " +
|
|
|
- "|| this(com.chelvc.framework.database.support.EnhanceMapper)")
|
|
|
- public Object routing(ProceedingJoinPoint point) throws Throwable {
|
|
|
- return super.routing(point);
|
|
|
}
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 动态数据源拦截器兼容实现
|
|
|
+ * 数据源路由
|
|
|
+ *
|
|
|
+ * @param point 方法拦截点
|
|
|
+ * @return 方法调用结果
|
|
|
+ * @throws Throwable 方法调用异常
|
|
|
*/
|
|
|
- @Aspect
|
|
|
- @Order(Ordered.HIGHEST_PRECEDENCE)
|
|
|
- public static class CompatibleDynamicDatasourceRouter extends AbstractDynamicDatasourceRouter {
|
|
|
- public CompatibleDynamicDatasourceRouter(DynamicDataSourceProperties properties) {
|
|
|
- super(properties);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 数据源路由
|
|
|
- *
|
|
|
- * @param point 方法拦截点
|
|
|
- * @return 方法调用结果
|
|
|
- * @throws Throwable 方法调用异常
|
|
|
- */
|
|
|
- @Around("@within(org.springframework.stereotype.Service) " +
|
|
|
- "|| @within(org.apache.ibatis.annotations.Mapper) " +
|
|
|
- "|| @within(org.apache.dubbo.config.annotation.DubboService) " +
|
|
|
- "|| this(com.baomidou.mybatisplus.core.mapper.BaseMapper) " +
|
|
|
- "|| this(com.chelvc.framework.database.support.EnhanceMapper)")
|
|
|
- public Object routing(ProceedingJoinPoint point) throws Throwable {
|
|
|
- return super.routing(point);
|
|
|
+ @Around("@within(org.springframework.stereotype.Service) " +
|
|
|
+ "|| @within(org.apache.ibatis.annotations.Mapper) " +
|
|
|
+ "|| this(com.baomidou.mybatisplus.core.mapper.BaseMapper) " +
|
|
|
+ "|| this(com.chelvc.framework.database.support.EnhanceMapper)")
|
|
|
+ public Object routing(ProceedingJoinPoint point) throws Throwable {
|
|
|
+ DynamicDataSourceContextHolder.push(this.lookupDatasourceKey(point));
|
|
|
+ try {
|
|
|
+ return point.proceed(point.getArgs());
|
|
|
+ } finally {
|
|
|
+ DynamicDataSourceContextHolder.poll();
|
|
|
}
|
|
|
}
|
|
|
}
|