springboot动态切换数据源,注解在dao层接口上无效,但在方法上有效。

springboot动态切换数据源,注解在dao层接口上无效,但在方法上有效。

这样就是有效的

img

注在类上就无效

img

这是切面逻辑,注解在类上的时候targetDataSource和methodDataSource都是null,注解在方法上的时候,methodDataSource就能取到注解,这是为什么呢。搜了一天没搜到答案。

img

img

aop代理机制导致的吧 aop不能直接代理接口 只能代理实现接口的类

methodDataSource 本来就是取的方法上的注解
targetDataSource 你要debug 看一下 targetClass 实际是啥

【以下回答由 GPT 生成】

问题原因

问题的原因是在Spring中,类级别上的注解不会被继承到方法级别上。这意味着即使在dao接口上使用了数据源注解,方法级别上的注解仍然会生效,并覆盖类级别的注解。

解决方案

要解决这个问题,我们可以通过在Spring中使用AOP(面向切面编程)来实现动态切换数据源。

首先,我们需要创建一个切面类,用于将数据源注解应用到类和方法上。切面类中的代码如下所示:

@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("@within(com.example.DataSource) || @annotation(com.example.DataSource)")
    public void dataSourcePointcut() {
    }

    @Before("dataSourcePointcut()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> targetClass = joinPoint.getTarget().getClass();
        DataSource classDataSource = targetClass.getAnnotation(DataSource.class);
        DataSource methodDataSource = signature.getMethod().getAnnotation(DataSource.class);

        if (methodDataSource != null) {
            DynamicDataSource.setDataSource(methodDataSource.value());
        } else if (classDataSource != null) {
            DynamicDataSource.setDataSource(classDataSource.value());
        } else {
            DynamicDataSource.setDataSource("default");
        }
    }

    @After("dataSourcePointcut()")
    public void after(JoinPoint joinPoint) {
        DynamicDataSource.clearDataSource();
    }
}

上面的代码使用@within@annotation注解来定义切点,它会匹配带有@DataSource注解的类和方法。

在切面类中,我们在方法执行之前通过@Before注解来设置数据源,并通过DynamicDataSource.setDataSource()方法来切换数据源。在方法执行之后,我们通过@After注解来清除数据源设置。

接下来,我们需要创建一个动态数据源类,用于管理多个数据源。它应该是一个继承了AbstractRoutingDataSource的类,并重写了determineCurrentLookupKey()方法。

@Component
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        dataSourceHolder.set(dataSource);
    }

    public static void clearDataSource() {
        dataSourceHolder.remove();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceHolder.get();
    }
}

上面的代码中,我们使用了ThreadLocal来保存当前线程使用的数据源。setDataSource()方法用于设置数据源,clearDataSource()方法用于清除数据源。在determineCurrentLookupKey()方法中,我们通过ThreadLocal来获取当前线程使用的数据源。

最后,我们还需要在Spring配置文件中配置数据源和扫描切面类。

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DynamicDataSource dynamicDataSource(DataSource primaryDataSource, DataSource secondaryDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource);
        targetDataSources.put("secondary", secondaryDataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(primaryDataSource);

        return dataSource;
    }

    @Bean
    public DataSourceAspect dataSourceAspect() {
        return new DataSourceAspect();
    }

}

上面的代码中,我们配置了两个数据源(primary和secondary),并通过targetDataSources属性来设置数据源映射。setDefaultTargetDataSource()方法用于设置默认的数据源。

通过配置DataSourceAspect类和DynamicDataSource类,我们可以启用AOP和动态数据源切换。

现在,当在dao接口上使用@DataSource注解时,数据源将会根据注解的值动态切换。

希望以上解决方案能解决你的问题。如果还有任何疑问,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^