SpringBoot AOP+注解方式实现多数据源切换可能遇到的问题


AOP+注解方式实现多数据源原理

通过ThreadLocal的线程隔离性将设线程与数据源ID进行绑定:

  • 若不设置则使用默认数据源
  • 若设置则使用该数据源ID对应的数据源(注意:使用完后需要清除该数据源ID)

可能遇到的问题

情景1:指定数据源的请求中发生报错

问题描述:指定数据源的请求中发生报错,后面未指定数据源的请求却使用了该指定数据源(应该使用默认数据源)。

代码:

@Slf4j
@Aspect
@Order(-2)
@Component
public class DataSourceAspect {
    
    

    @Pointcut("@annotation(com.joker.datasource.aopannotation.DataSource) || @within(com.joker.datasource.aopannotation.DataSource)")
    public void run(){
    
    

    }

    @Around("run()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    
    
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 方法上获取
        // AnnotatedElementUtils.hasAnnotation()
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.isNull(dataSource)) {
    
    
            // 类上获取
            dataSource =  AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
        }
        // 设置数据源
        DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        Object obj = point.proceed();
        // 清除数据源
        DynamicDataSourceContextHolder.clearDataSourceType();
        return obj;
    }
}

问题分析:

  1. 指定数据源的请求中发生报错,导致清除数据源ID未执行,当前线程thread1仍然绑定了该数据源;
  2. 由于接口请求使用的线程是通过线程池来管理的,后续该线程thread1可能会继续分配给其它请求使用
  3. 如果后续请求使用了该线程thread1且未指定数据源,使用的仍然是前面绑定的数据源,导致使用错数据源(原本应该使用默认数据源)
  4. 但如果后续请求使用了该线程thread1但指定了数据源,则不会有问题

情景2:指定数据源的请求中使用新的线程

问题描述:指定数据源的请求中使用新的线程,导致指定数据源无效,使用的是默认数据源。

问题分析:因为数据源是和线程绑定的,即使在当前线程绑定了指定数据源,但如果在请求中使用了新的线程,新线程是没有绑定数据源的(默认使用默认数据源)。使用新线程的一下场景:

  1. 使用了new Thread()创建的新线程,在新线程中使用数据源
  2. 使用了线程池,通过线程池中的线程来使用数据源
  3. 使用了Java8中list.parallelStream()来并行处理(多线程处理),处理过程中使用数据

猜你喜欢

转载自blog.csdn.net/JokerLJG/article/details/131406803
今日推荐