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;
}
}
问题分析:
- 指定数据源的请求中发生报错,导致清除数据源ID未执行,当前线程thread1仍然绑定了该数据源;
- 由于接口请求使用的线程是通过线程池来管理的,后续该线程thread1可能会继续分配给其它请求使用
- 如果后续请求使用了该线程thread1且未指定数据源,使用的仍然是前面绑定的数据源,导致使用错数据源(原本应该使用默认数据源)
- 但如果后续请求使用了该线程thread1但指定了数据源,则不会有问题
情景2:指定数据源的请求中使用新的线程
问题描述:指定数据源的请求中使用新的线程,导致指定数据源无效,使用的是默认数据源。
问题分析:因为数据源是和线程绑定的,即使在当前线程绑定了指定数据源,但如果在请求中使用了新的线程,新线程是没有绑定数据源的(默认使用默认数据源)。使用新线程的一下场景:
- 使用了new Thread()创建的新线程,在新线程中使用数据源
- 使用了线程池,通过线程池中的线程来使用数据源
- 使用了Java8中list.parallelStream()来并行处理(多线程处理),处理过程中使用数据