java自定义注解支持SpEL表达式

306 阅读2分钟

写在前面

在Java开发中,自定义注解为代码提供了丰富的元数据支持,而Spring表达式语言(SpEL)则允许在运行时动态查询和操作对象。将这两者结合,可以极大地增强代码的动态性和灵活性。

核心代码实现

/**
 * 用于SpEL表达式解析.
 */
private SpelExpressionParser parser = new SpelExpressionParser();
/**
 * 用于获取方法参数定义名字.
 */
private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();


public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
    Method method = methodSignature.getMethod();
    if(method.isAnnotationPresent(DynamicTable.class)){
        DynamicTable dynamicTable =  (DynamicTable) method.getAnnotation(DynamicTable.class);
        String key = dynamicTable.separateKey();
        // 获取参数
        Object[] args = joinPoint.getArgs();

        Expression expression = parser.parseExpression(key);
        // 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        // spring的表达式上下文对象
        EvaluationContext context = new StandardEvaluationContext();
        // 给上下文赋值
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }

        RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
            put(TABLE_NAME, dynamicTable.tableName());
            put(TABLE_SUFFIX, expression.getValue(context));
        }});
        Object proceed = joinPoint.proceed();
        RequestDataHelper.removeRequestData();
        return proceed;
    }else{
        Object proceed = joinPoint.proceed();
        return proceed;
    }

代码说明:

  • SpelExpressionParser 用于解析SpEL表达式。
  • StandardEvaluationContext 用于创建表达式的上下文环境。
  • LocalVariableTableParameterNameDiscoverer 用于获取方法参数的名字。 实现步骤:
  1. 解析表达式:parser.parseExpression(key)
  2. 表达式上下文环境设置值:
        // spring的表达式上下文对象
        EvaluationContext context = new StandardEvaluationContext();
        // 给上下文赋值
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        

3.计算表达式结果:expression.getValue(context)

备注:上述代码是我在实现分表功能用的一个注解切面实现

在注解时使用SpEL表达式

  • 引用方法入参: #参数名
  • 引用方法的pojo类型的属性参数:#pojo.property
  • 调用静态方法:T(com.xx.xxx.xxxx).functionName(参数1,参数2.....). 其中括号里填“被调用的方法所属类的包路径”,可不是当前使用这个注解的类的包路径,并且调用的方法必须是静态方法,否则会报“找不带该方法”异常.
  • 调用非静态方法:new com.xx.xxx.xxxx().functionName(参数1,参数2.....)`. 调用非静态方法,语法非常像new一个对象的写法,但不同的是不能直接“new 类名”,必须是“new 类的完整包路径+类名”,即使是调用的方法就在当前类中,也要严格按照规定格式写,否则一定报错。
  • 引用枚举:`T(com.xx.xxx.xxxx).DEMO.getCode() . 和调用静态方法类似

参考:

mp.weixin.qq.com/s/1dN8wEpWQ…