Spring AOP自定义注释

时间:2018-02-06 13:31:57

标签: java spring annotations aop

我正在尝试实现自定义注释和方面,它将在验证之前将路径变量插入到请求主体中。 现在它看起来像这样......

@Aspect
@Component
public class AddParameterToFormAspect {

@Before("@annotation(addParameterToForm)")
public void addParameterToForm(JoinPoint joinPoint, AddParameterToForm addParameterToForm) {
    String form = addParameterToForm.form();
    String pathVariable = addParameterToForm.pathVariable();
    CodeSignature methodSignature = (CodeSignature) joinPoint.getSignature();
    List<String> methodParamNames = Arrays.asList(methodSignature.getParameterNames());
    int formIndex = 0;
    int pathVariableIndex = 0;

    for(String s : methodSignature.getParameterNames()) {
        if(s.equals(form)) {
            formIndex = methodParamNames.indexOf(s);
        }
        if(s.equals(pathVariable)) {
            pathVariableIndex = methodParamNames.indexOf(s);
        }
    }

    Object[] methodArgs = joinPoint.getArgs();
    Object formObject = methodArgs[formIndex];
    Field pathVariableObject;

    try {
        pathVariableObject = formObject.getClass().getDeclaredField(pathVariable);
        pathVariableObject.setAccessible(true);
        pathVariableObject.set(formObject, methodArgs[pathVariableIndex]);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

}

工作注释的控制器示例...

@PostMapping("/test/{username}")
@AddParameterToForm(pathVariable = "username", form = "user")
public String test(@PathVariable String username, @RequestBody User user) {
    return user.getUsername();
}

验证的控制器示例不起作用......

@PostMapping("/{domainCode}")
@AddParameterToForm(pathVariable = "domainCode", form = "userAddForm")
public ResponseEntity<UserDto> saveUserForDomain(@PathVariable(name="domainCode") String domainCode, @RequestBody @Valid  final UserAddForm userAddForm, BindingResult results) {...}

添加路径变量以形成作品,但似乎@Valid不再有效,问题可能在连接点表达式中......如何在验证之前进行建议然后验证?

1 个答案:

答案 0 :(得分:1)

更改@Before建议中的方法参数并不适用。在调用@Around之前,您应该使用thisJoinPoint.proceed()建议来更改参数。这是因为在调用thisJoinPoint.getArgs()时,您获得了基本类型参​​数的副本,您无法在before-advice中操作原始文件。你很幸运,你想在这种情况下操纵对象类型,这就是它工作的原因。使用around-advice可以让你将全新的参数传递给方法或只是操作原始对象,你可以自由选择。

此外,您应该 - 尽可能 - 使用args()将您感兴趣的方法参数绑定到通知参数,以便能够以非神秘且类型安全的方式与它们进行交互。创建局部变量并为其分配一些值不会影响相同类型的方法参数。为什么要这样?

如果此解释不够全面,请随时提出后续问题。然后我也可以为你添加一些示例代码。

问题编辑后更新:

在仔细检查了您的代码之后,除了我今天早些时候在您的问题评论中的评论之外,忽略了方面代码的内容,您的实际问题是@Valid的验证检查原因在执行方法之前执行注释。即验证的不是在方面完成其工作(填充目标对象中的成员字段)之后的状态,而是在方面运行之前的状态。这实际上与this question中讨论的问题相同,另见M. Deinum和我的建议如何解决它:

  • 也许您想尝试完整AspectJ via LTW (load-time weaving)并查看{A}}切入点而不是Spring AOP使用的隐式call()切入点是否解决了问题。您将编织到调用代码(方法调用)而不是被调用者(方法执行)本身。有可能在执行验证之前发生这种情况。

  • 更类似Spring的解决方法是使用Spring拦截器(M. Deinum提及execution())而不是方面。其他人也有example的链接。

话虽如此,我仍然建议重构代码,以免在方法参数名或类成员名上使用反射和匹配字符串。我认为你也可以通过将方法参数的切入点与HandlerInterceptor@RequestBody注释相匹配来摆脱自定义注释。