Spring 在@Around通知中处理带@RequestBody和不带@RequestBody的请求
我有这样的基于方面的日志记录:Spring 在@Around通知中处理带@RequestBody和不带@RequestBody的请求,spring,spring-boot,aspectj,spring-aop,Spring,Spring Boot,Aspectj,Spring Aop,我有这样的基于方面的日志记录: @Pointcut(“@annotation(Loggable)”) public void loggableAnnotation(){} @环绕(“loggableAnnotation()”) 公共对象simpleProcess(ProceedingJoinPoint joinPoint)抛出可丢弃的{ 返回此.processWithBody(连接点,null); } @大约(“loggableAnnotation()&&args(@org.springfram
@Pointcut(“@annotation(Loggable)”)
public void loggableAnnotation(){}
@环绕(“loggableAnnotation()”)
公共对象simpleProcess(ProceedingJoinPoint joinPoint)抛出可丢弃的{
返回此.processWithBody(连接点,null);
}
@大约(“loggableAnnotation()&&args(@org.springframework.web.bind.annotation.RequestBody,…))
公共对象processWithBody(ProceedingJoinPoint joinPoint,Object body)抛出Throwable{
//做事
}
当我使用@RequestBody
执行请求时,会触发通知processWithBody()
。但是当我执行没有@RequestBody
(仅@PathVariable
和@RequestParam
)的请求时,simpleProcess()
不会触发,而是在processWithBody()
中,我接收路径变量值作为body
参数
为什么会发生这种情况?我如何以不同的方式处理两种类型的请求(如果可能,请使用相同的建议)?您犯了三个基本错误:
- 您正试图从
中匹配参数参数注释,但没有效果,这就是为什么args()
匹配不需要的参数并将其绑定到processWithBody(…)
。它应该被传输到body
切入点执行()
- 您的切入点语法是错误的,即使您将其转移到
,例如execution()
将匹配参数的类型(!)是否有execution(**(@org.springframework.web.bind.annotation.RequestBody*,…)
注释,不是参数本身。@RequestBody
为了实现这一点,您需要将参数本身放入括号中,如
,即(*)
执行(**(@org.springframework.web.bind.annotation.RequestBody(*),…)
- 您必须确保切入点是互斥的,否则多个通知应在同一接合点上匹配。准确地说,您需要区分以下情况:
- 方法由
注释,第一个方法参数由@Loggable
注释@RequestBody
- 方法由
注释,第一个方法参数不是由@Loggable
注释@RequestBody
- 方法由
注释,无任何参数@Loggable
- 方法由
package de.scrum\u master.app;
导入静态java.lang.annotation.ElementType.METHOD;
导入静态java.lang.annotation.RetentionPolicy.RUNTIME;
导入java.lang.annotation.Retention;
导入java.lang.annotation.Target;
@保留(运行时)
@目标(方法)
public@interface Loggable{}
package de.scrum\u master.app;
导入org.springframework.web.bind.annotation.RequestBody;
公共类应用程序{
公共静态void main(字符串[]args){
应用程序=新应用程序();
应用程序。doNotLogMe(“foo”,11);
application.doNotLogMeEither();
申请书。doNotLogMeEither(“foo”,11);
应用程序。logMe(“foo”,11);
申请书.logMeToo(“foo”,11);
application.logMeToo();
}
public void doNotLogMe(@RequestBody String body,int number){}
public void doNotLogMeEither(){}
public void doNotLogMeEither(字符串体,整数){}
@Loggable public void logMe(@RequestBody String body,int number){}
@Loggable public void logMeToo(字符串体,整数){}
@Loggable public void logMeToo(){}
}
方面:
正如您所看到的,我使用的是区分上述三种情况的方法,同时也满足了您对一种常见助手方法的需求,我称之为logIt(…)
。在那里,您可以放置所有想要使用的复杂日志记录内容,而不会在您的advice方法中有任何重复的代码
package de.scrum\u master.aspect;
导入org.aspectj.lang.ProceedingJoinPoint;
导入org.aspectj.lang.annotation.Around;
导入org.aspectj.lang.annotation.Aspect;
导入org.aspectj.lang.annotation.Pointcut;
@面貌
公共类MyAspect{
@切入点(“注释(de.scrum\u master.app.Loggable)”)
public void loggableAnnotation(){}
@周围(
“loggableAnnotation()&&”+
“执行(**)”
)
公共对象simpleProcessWithoutParameters(ProceedingJoinPoint joinPoint)抛出可丢弃的{
返回logIt(连接点,null);
}
@周围(
“loggableAnnotation()&”+
“执行(**(!@org.springframework.web.bind.annotation.RequestBody(*),…)”
)
公共对象simpleProcessWithParameters(ProceedingJoinPoint joinPoint)抛出可丢弃的{
返回logIt(连接点,null);
}
@周围(
“loggableAnnotation()&&”+
“执行(**(@org.springframework.web.bind.annotation.RequestBody(*),…)&”+
“args(正文,…)”
)
公共对象processWithBody(ProceedingJoinPoint joinPoint,Object body)抛出Throwable{
返回登录(连接点、主体);
}
私有对象logIt(ProceedingJoinPoint,objectbody)抛出Throwable{
System.out.println(连接点+“->”+主体);
返回joinPoint.procedure();
}
}
控制台日志:
execution(void de.scrum\u master.app.Application.logMe(String,int))->foo
执行(void de.scrum_master.app.Application.logMeToo(String,int))->null
执行(void de.scrum_master.app.Application.logMeToo())->null
注:execution(**(@MyAnn*)
和execution(**(@MyAnn(*))
之间的区别是微妙的,因此很棘手。不幸的是,它没有正确地记录在应该放在哪里。准确地说,后一种情况根本没有记录,仅