处理@About建议中包含和不包含@RequestBody的请求 [英] Process requests with and without @RequestBody in the @Around advice
问题描述
我有这样一个基于方面的日志记录:
@Pointcut("@annotation(Loggable)")
public void loggableAnnotation() {}
@Around("loggableAnnotation()")
public Object simpleProcess(ProceedingJoinPoint joinPoint) throws Throwable {
return this.processWithBody(joinPoint, null);
}
@Around("loggableAnnotation() && args(@org.springframework.web.bind.annotation.RequestBody body,..)")
public Object processWithBody(ProceedingJoinPoint joinPoint, Object body) throws Throwable {
// do things
}
当我用@RequestBody
执行请求时,它工作得很好,建议processWithBody()
被触发。但当我执行没有@RequestBody
(仅@PathVariable
和@RequestParam
)的请求时,simpleProcess()
不会被触发,相反,在processWithBody()
中,我会收到路径变量值作为body
参数。
为什么会发生这种情况,以及如何以不同的方式处理两种类型的请求(如果可能,在相同的建议中)?
推荐答案
您犯了三个基本错误:
您正在尝试匹配
args()
中的参数实参批注,但在那里没有效果,这就是processWithBody(..)
匹配不需要的参数并将其绑定到body
的原因。应将其传输到execution()
切入点。您的切入点语法是错误的,即使您将它转移到
execution()
,即execution(* *(@org.springframework.web.bind.annotation.RequestBody *, ..))
如果类型(!)参数的具有@RequestBody
注释,而不是参数本身。
为此,您需要将参数本身放在括号中,如(*)
,即execution(* *(@org.springframework.web.bind.annotation.RequestBody (*), ..))
。您必须确保切入点是互斥的,否则多个建议将在同一连接点上匹配。准确地说,您需要区分以下情况:
@Loggable
批注的方法,其中第一个方法参数由@RequestBody
批注@Loggable
批注的方法不@RequestBody
批注的第一个方法参数- 由
@Loggable
批注的方法,不带任何参数
以下是纯Java+AspectJ(没有Spring或Spring AOP)的示例,但是在Spring AOP中方面语法应该相同:
注释+驱动程序应用程序:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(METHOD)
public @interface Loggable {}
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RequestBody;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.doNotLogMe("foo", 11);
application.doNotLogMeEither();
application.doNotLogMeEither("foo", 11);
application.logMe("foo", 11);
application.logMeToo("foo", 11);
application.logMeToo();
}
public void doNotLogMe(@RequestBody String body, int number) {}
public void doNotLogMeEither() {}
public void doNotLogMeEither(String body, int number) {}
@Loggable public void logMe(@RequestBody String body, int number) {}
@Loggable public void logMeToo(String body, int number) {}
@Loggable public void logMeToo() {}
}
方面:
如您所见,我使用了区分上述三种情况的方法,还满足了您对我称为logIt(..)
的通用辅助方法的需求。在那里,您可以放置您想要使用的所有复杂的日志记录内容,而不会在建议方法中包含任何重复的代码。
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyAspect {
@Pointcut("@annotation(de.scrum_master.app.Loggable)")
public void loggableAnnotation() {}
@Around(
"loggableAnnotation() && " +
"execution(* *())"
)
public Object simpleProcessWithoutParameters(ProceedingJoinPoint joinPoint) throws Throwable {
return logIt(joinPoint, null);
}
@Around(
"loggableAnnotation() && " +
"execution(* *(!@org.springframework.web.bind.annotation.RequestBody (*), ..))"
)
public Object simpleProcessWithParameters(ProceedingJoinPoint joinPoint) throws Throwable {
return logIt(joinPoint, null);
}
@Around(
"loggableAnnotation() && " +
"execution(* *(@org.springframework.web.bind.annotation.RequestBody (*), ..)) && " +
"args(body, ..)"
)
public Object processWithBody(ProceedingJoinPoint joinPoint, Object body) throws Throwable {
return logIt(joinPoint, body);
}
private Object logIt(ProceedingJoinPoint joinPoint, Object body) throws Throwable {
System.out.println(joinPoint + " -> " + body);
return joinPoint.proceed();
}
}
控制台日志:
execution(void de.scrum_master.app.Application.logMe(String, int)) -> foo
execution(void de.scrum_master.app.Application.logMeToo(String, int)) -> null
execution(void de.scrum_master.app.Application.logMeToo()) -> null
附注:execution(* *(@MyAnn *))
和execution(* *(@MyAnn (*)))
之间的区别很微妙,因此很棘手。遗憾的是,它没有正确地记录在它应该在的位置here。准确地说,后一种情况根本没有文档记录,可能只是在一些AspectJ发行说明中,当然也在单元测试中。但普通用户不会在那里查看。
这篇关于处理@About建议中包含和不包含@RequestBody的请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!