Skip to content

Spring Boot 自定义注解结合 Spel 表达式实现动态解析

在实际开发中,经常需要根据方法参数动态生成某些值,比如日志记录、参数校验、缓存 key 生成等。SpEL(Spring Expression Language)强大的表达式能力可以帮我们轻松实现这些需求。本文将介绍如何通过自定义注解结合 SpEL 实现动态解析。

一、需求分析

目标:

  • 通过自定义注解,动态解析方法参数值。
  • 支持 SpEL 表达式,例如:#param.name 获取对象属性值。
  • 如果不是 SpEL 表达式,直接取值。

二、实现步骤

  1. 创建自定义注解
java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpelValue {
    /**
     * SpEL 表达式或参数字段名称
     */
    String value();
}
  • @Target:指定注解使用的位置为方法。
  • @Retention:运行时保留,便于在 AOP 中动态获取。
  • @Documented:生成文档时包含注解信息。
  1. 自定义切面

切面需要拦截方法,并根据注解定义的 SpEL 表达式进行解析和处理。

java
@Slf4j
@Aspect
@Component
public class SpelValueAspect {


    @Around("@annotation(spelValue)")
    public Object aroundSpel(ProceedingJoinPoint joinPoint, SpelValue spelValue) throws Throwable {
        String value = spelValue.value();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        if (StringUtils.isEmpty(value)) {
            value = className + ":" + methodName;
        }
        String key = SpelExpressionUtils.parserSpel(method, joinPoint.getArgs(), value, String.class, className + ":" + methodName);
        log.info("获取到注解上value的解析出来的值为 {}",key);
        return joinPoint.proceed();
    }
}

3. 测试

定义一个服务类

java

public interface ITestService {

    void test(RenderBO renderBO);

    void test(String text);


    void test(String text,String input);


    void test(String text,String input,String output);
}

@Service
public class TestService implements ITestService{

    @SpelValue("#renderBO.textKey + ' ' + 'cm'")
    @Override
    public void test(RenderBO renderBO) {
        return;
    }

    @Override
    @SpelValue("#text")
    public void test(String text) {

    }

    @SpelValue("not spel")
    @Override
    public void test(String text, String input) {

    }

    @SpelValue
    @Override
    public void test(String text, String input, String output) {

    }

}

执行之后输出如下:

text
2025-01-16 17:16:36.435  INFO 25080 --- [           main] c.s.b.annotation.spel.SpelValueAspect    : 获取到注解上value的解析出来的值为 key cm
2025-01-16 17:16:36.439  INFO 25080 --- [           main] c.s.b.annotation.spel.SpelValueAspect    : 获取到注解上value的解析出来的值为 xxxxxx
2025-01-16 17:16:36.440  INFO 25080 --- [           main] c.s.b.annotation.spel.SpelValueAspect    : 获取到注解上value的解析出来的值为 not spel
2025-01-16 17:16:36.441  INFO 25080 --- [           main] c.s.b.annotation.spel.SpelValueAspect    : 获取到注解上value的解析出来的值为 com.ssn.boot.service.TestService:test

总结

通过以上方式,我们实现了一个支持 SpEL 动态解析的自定义注解。这种方式能够大大增强代码的灵活性,适合用于日志记录、动态缓存键生成、方法调用监控等场景。希望本文对你有所帮助!

Released under the MIT License.