我正在尝试实现自定义注释和方面,它将在验证之前将路径变量插入到请求主体中。
现在它看起来像这样......
@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不再有效,问题可能在连接点表达式中......如何在验证之前进行建议然后验证?
答案 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
注释相匹配来摆脱自定义注释。