先说问题
先说下网上有很多文章写很多demo,参考博客:https://blog.csdn.net/zl_momomo/article/details/82851134,下面是核心代码:
- 首先定义一个ThrealLocal String类型来存储数据源
- 做一个切面,设置这个数据源,方法执行完,把ThreadLocal给清理掉。
- spring 从当前ThreadLocal获取数据源
这个可以满足我们大多数场景,来看一个问题:
思考下C方法会是哪个数据源?
答案是方法c会执行默认数据源,但是问题是我们在调用c方法上明明标记了a数据源,不应该走a数据源么。方法c执行完,ThreadLocal就没有了,其实我们要还能从ThreadLocal中获取到a数据源。
基于这样的场景好像,只存一个string结构就不满足了。
这样就考虑到,既然是切到注解上,只有这个注解的方法执行完了,才能把ThreadLocal的值给删掉,于是就想到了栈结构。
解决问题
public final class DynamicDataSourceContextHolder {
private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedThreadLocal<Deque<String>>("dynamic-datasource") {
@Override
protected Deque<String> initialValue() {
return new ArrayDeque<>();
}
};
private DynamicDataSourceContextHolder() {
}
/**
* 获得当前线程数据源
*
* @return 数据源名称
*/
public static String peek() {
return LOOKUP_KEY_HOLDER.get().peek();
}
/**
* 设置当前线程数据源
* <p>
* 如非必要不要手动调用,调用后确保最终清除
* </p>
*
* @param ds 数据源名称
*/
public static void push(String ds) {
LOOKUP_KEY_HOLDER.get().push(StringUtils.isEmpty(ds) ? "" : ds);
}
/**
* 清空当前线程数据源
* <p>
* 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称
* </p>
*/
public static void poll() {
Deque<String> deque = LOOKUP_KEY_HOLDER.get();
deque.poll();
if (deque.isEmpty()) {
LOOKUP_KEY_HOLDER.remove();
}
}
/**
* 强制清空本地线程
* <p>
* 防止内存泄漏,如手动调用了push可调用此方法确保清除
* </p>
*/
public static void clear() {
LOOKUP_KEY_HOLDER.remove();
}
}
- 数据源注解拦截器里面调用push、poll方法,控制添加、删除元素。
- spring获取动态数据源只需要调用peek方法,不需要弹出。