SpringBootAOP + SpringEL表达式 实现动态传参(基于MethodBasedEvaluationContext)

250 阅读1分钟

SpringBootAOP + SpringEL表达式 实现动态传参(基于MethodBasedEvaluationContext)

SPEL表达式

Spring 提供的EL表达式解析特性强化了注解能力。能做到注解中的属性值动态地从方法参数中获取,而不是简单地设置固定值。

AOP依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
  <version>2.1.17.RELEASE</version>
</dependency>

自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface LogAnn {
    // 操作人
    String operator() default "";

    // 业务单号
    String bizNo() default "";
    
    // 操作 
    BusinessAction action() default BusinessAction.OTHER;
}

操作字段的枚举类型

public enum BusinessAction {
    /**
     * 其它
     */
    OTHER,

    /**
     * 新增
     */
    INSERT,

    /**
     * 修改
     */
    UPDATE,

    /**
     * 删除
     */
    DELETE,

    /**
     * 授权
     */
    GRANT,

    /**
     * 导出
     */
    EXPORT,

    /**
     * 导入
     */
    IMPORT,

    /**
     * 强退
     */
    FORCE,

    /**
     * 生成代码
     */
    GENCODE,

    /**
     * 清空数据
     */
    CLEAN,
}

注解的使用方式

其中LogTestModel用于测试,类中包含userName和orderId字段。

@PostMapping("LogAnn")
@LogAnn(operator = "#logTestModel.userName", bizNo = "#logTestModel.orderId", action = BusinessAction.OTHER)
public String LogAnn(@RequestBody LogTestModel logTestModel) {
    System.out.println("测试");
    return "success";
}

Aspect代码

拦截LogAnn注解作为切点

使用MethodBasedEvaluationContext进行SPEL解析

@Slf4j
@Aspect
@Component
public class LogAspect {
    /**
     * 用于获取方法参数定义名字.
     */
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    /**
     * 用于SpEL表达式解析.
     */
    private static final SpelExpressionParser parser = new SpelExpressionParser();

    @Pointcut("@annotation(com.hong.demo.common.annotate3.LogAnn)")
    public void LogAnn() {
    }

    @Around("LogAnn()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取目标类
        Object target = joinPoint.getThis();
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
        // 获取方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 可解决代理对象问题
        Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        LogAnn logAnn = method.getAnnotation(LogAnn.class);
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(target, targetMethod, args, parameterNameDiscoverer);
        // 处理注解上的值
        String bizNo = logAnn.bizNo();
        String bizNoValue = getExpValue(context, bizNo);
        log.info("bizNo:{}", bizNoValue);
        String operator = logAnn.operator();
        String operatorValue = getExpValue(context, operator);
        log.info("operator:{}", operatorValue);
        // to do 
        return joinPoint.proceed();
    }

    private String getExpValue(MethodBasedEvaluationContext context, String expr) {
        Expression expression = this.parser.parseExpression(expr);
        return (String) expression.getValue(context);
    }
}

测试结果

image-20231209124414983.png

image-20231209124458721.png

以上就是在AOP中使用SPEL进行动态参数的demo,最后不要忘记在配置中打开“@EnableAspectJAutoProxy”。