You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring AOP中使用SpEL表达式解析JoinPoint方法参数嵌套对象的问题与最佳实践咨询

Spring AOP中使用SpEL表达式解析JoinPoint方法参数嵌套对象的问题与最佳实践咨询

我现在遇到了一个在Spring AOP场景下,使用SpEL表达式解析方法参数中嵌套对象的问题,想请教大家解决方案和对应的最佳实践:

场景说明

我创建了一个自定义注解,希望它能通用处理嵌套对象、简单整数等不同类型的参数,注解定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Annotation {
    String template();
    String[] parameters() default {};
}

我会在方法上这样使用这个注解:

@Annotation(
        template = "Test-{0}-{1}-{2}",
        parameters = {"#request.playroundUid.fundraiserId", "#request.selectionPlayroundNumber", "#request.playroundUid.playroundType"}
)
public TestResponse test(Request request) 

问题代码与报错

为了解析注解中的参数表达式,我在切面里写了一个generateName方法:

public static String generateName(ProceedingJoinPoint joinPoint, Annotation annotation) {
    String template = annotation.template();
    Object[] args = joinPoint.getArgs();
    String[] parameters = annotation.parameters();

    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    String[] parameterNames = signature.getParameterNames();

    // Create a map of parameter names -> values
    Map<String, Object> paramMap = new HashMap<>();
    for (int i = 0; i < parameterNames.length; i++) {
        paramMap.put(parameterNames[i], args[i]);  
    }

    // DEBUG: Print out the map to verify correct parameter storage
    System.out.println("Parameter Map: " + paramMap);

    // SpEL context
    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setVariables(paramMap); // Bind method parameters (e.g., request)

    ExpressionParser parser = new SpelExpressionParser();

    for (int i = 0; i < parameters.length; i++) {
        String parameter = parameters[i];
        if (parameter.startsWith("#")) {
            try {
                String expression = parameter.substring(1); // Remove "#"

                // DEBUG: Print out the expression being evaluated
                System.out.println("Evaluating SpEL expression: " + expression);

                // Evaluate the SpEL expression directly
                Object evaluatedValue = parser.parseExpression(expression).getValue(context);

                if (evaluatedValue == null) {
                    throw new RuntimeException("SpEL expression '" + parameter + "' evaluated to null. Check field access.");
                }

                template = template.replace("{" + i + "}", evaluatedValue.toString());
            } catch (Exception e) {
                throw new RuntimeException("Failed to evaluate SpEL expression: " + parameter, e);
            }
        } else {
            template = template.replace("{" + i + "}", args[i] != null ? args[i].toString() : "null");
        }
    }
    return template;
}

但代码中parser.parseExpression(expression).getValue(context);这一行总是解析失败,报错信息如下:

java.lang.RuntimeException: Failed to evaluate SpEL expression: #request.playroundUid.fundraiserId
    at com.novamedia.nl.beehive.participationadministration.util.AnnotationUtil.generateName(AnnotationUtil.java:112)
    at com.novamedia.nl.beehive.participationadministration.aspect.AspectTest.shouldGenerateCorrectNameForComplexParameters(AspectTest.java:194)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)   
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)   
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'request' cannot be found on null  
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:225)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:112)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:100)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:60)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:96)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:114)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:273)
    at com.novamedia.nl.beehive.participationadministration.util.AnnotationUtil.generateLockName(AnnotationUtil.java:104)
    ... 4 more

单元测试代码

我的单元测试逻辑如下:

@Test
void shouldGenerateCorrectNameForComplexParameters() throws NoSuchMethodException {
    // Arrange
    Annotation annotation = mock(Annotation.class);
    when(annotation.template()).thenReturn("CreateTicketsForPlayround-{0}-{1}-{2}");
    when(annotation.parameters()).thenReturn(new String[]{"#request.playroundUid.fundraiserId", "#request.selectionPlayroundNumber", "#request.playroundUid.playroundType"});

    Method mockMethod = MyTestService.class.getMethod("createTicketsForPlayround", CreateTicketsForPlayroundRequest.class);
    when(methodSignature.getMethod()).thenReturn(mockMethod);
    when(methodSignature.getParameterNames()).thenReturn(new String[]{"request"});

    // Create a mock request with nested properties
    CreateTicketsForPlayroundRequest request = CreateTicketsForPlayroundRequest.builder()
            .selectionPlayroundNumber(1)
            .playroundUid(new PlayroundUid(1001, 1, PlayroundType.REGULAR, 1))
            .build();

    when(joinPoint.getArgs()).thenReturn(new Object[]{request});

    // Act
    String name = generateName(joinPoint, annotation);

    // Assert
    assertEquals("CreateTicketsForPlayround-1001-1-REGULAR", name);
}

我的问题

  • 怎么才能用SpEL表达式正确解析request这类嵌套对象的属性?
  • 这种场景下的最佳实践是什么?一定要用SpEL吗?有没有其他更合适的处理方式?

备注:内容来源于stack exchange,提问作者GaripTipici

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 10:08:04