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
相关产品推荐
相关产品推荐

