让自定义注解的属性支持el表达式

在一个需要实现动态数据源的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