在一个需要实现动态数据源的spring项目里,为了更方便地切换数据源,我选择了使用注解加AOP的方式自动处理切换工作。
1)创建SwitchDS注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SwitchDS { String value() default "'" + DEFAULT_DS + "'"; String DEFAULT_DS = "default"; }
2)创建切面处理类
@Slf4j
@Aspect
@Component
public class DataSourceAspect implements Ordered {
private ExpressionParser spelExpressionParser = new SpelExpressionParser();
private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
/**
* 解析el表达式
*
* @param point
* @param elStr
* @return
*/
private String elParser(ProceedingJoinPoint point, String elStr) {
Object[] args = point.getArgs();
String[] params = discoverer.getParameterNames(((MethodSignature) point.getSignature()).getMethod());
EvaluationContext context = new StandardEvaluationContext();
for (int len = 0; len < params.length; len++) {
context.setVariable(params[len], args[len]);
}
return spelExpressionParser.parseExpression(elStr).getValue(context, String.class);
}
@Around("@annotation(ds)")
public Object around(ProceedingJoinPoint point, SwitchDS ds) throws Throwable {
if (ds == null) {
//使用默认数据源
DynamicDataSource.use(SwitchDS.DEFAULT_DS);
log.debug("use default datasource");
} else {
//解析el表达式得到数据源名称
String dsName = elParser(point, ds.value());
DynamicDataSource.use(dsName);
log.debug("set datasource is " + dsName);
}
try {
return point.proceed();
} finally {
//用完及时清理该线程绑定的数据源
DynamicDataSource.clear();
log.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
3)注解使用示例
在ServiceImpl
类里面
@Override
@SwitchDS("#dsName")
public PageUtils queryPage(String dsName, Map<String, Object> params) { //TODO ...
}
@Override
@SwitchDS("#accessLog.dsName")
public Boolean saveRecord(AccessLogEntity accessLog) { //TODO ...
}
4)动态数据源路由实现类DynamicDataSource
@Primary
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
private final HikariDataSource defaultDataSource;
private final HashMap<String, HikariDataSource> MYSQL_INS = new HashMap<>();
@Autowired
public DynamicDataSource(HikariDataSource defaultDataSource) {
this.defaultDataSource = defaultDataSource;
MYSQL_INS.put(SwitchDS.DEFAULT_DS, defaultDataSource);
}
@Override
public void afterPropertiesSet() {
}
@Override
protected Object determineCurrentLookupKey() {
return CONTEXT_HOLDER.get();
}
@Override
protected DataSource determineTargetDataSource() {
DataSource dataSource = MYSQL_INS.get(CONTEXT_HOLDER.get());
return dataSource == null ? defaultDataSource : dataSource;
}
/**
* 判断一个数据源是否已连接
* @param dsName
* @return
*/
public Boolean has(String dsName) {
return MYSQL_INS.containsKey(dsName);
}
/**
* 添加新的数据源 * @param dsName * @param dataSource
*/
public void add(String dsName, HikariDataSource dataSource) {
HikariDataSource oldDataSource = MYSQL_INS.get(dsName);
MYSQL_INS.put(dsName, dataSource);
if (oldDataSource != null) {
oldDataSource.close();
}
}
/**
* 移除已添加的数据源
* @param dsName
*/
public void remove(String dsName) {
if (SwitchDS.DEFAULT_DS.equals(dsName)) {
return;
}
HikariDataSource dataSource = MYSQL_INS.remove(dsName);
if (dataSource != null) {
dataSource.close();
}
}
public static void use(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static void useDefault() {
CONTEXT_HOLDER.set(SwitchDS.DEFAULT_DS);
}
public static void clear() {
CONTEXT_HOLDER.remove();
}
}
参考链接:https://blog.csdn.net/u011305680/article/details/80271423