如何让 Spring @ControllerAdvice 与其他自定义 Spring @Aspect 一起工作?
How to get Spring @ControllerAdvice work with other custom Spring @Aspect?
我有一个使用 @Aspect
的自定义记录器,我希望它最后总是 运行 这样无论从控制器返回什么响应,它都会被记录到数据库中(所以我在这方面加上 @Order(1)
)。我还使用 @ControllerAdvice
编写了一个错误处理程序来处理所有意外异常,并使用 returns 500
和自定义响应主体编写了一个错误处理程序,我希望它也被记录器记录下来,所以我在上面放一个 @Order(2)
,但是看起来放 @Order
注释并没有安排 Spring Aspect 和 Spring ControllerAdvice 之间的顺序,那么如何让我的错误处理程序始终 运行 在记录器之前? (当然,没有将我的错误处理程序转到另一个 Spring 方面)
我一直在寻找和调试您的主要问题。我没有找到你关于 @Order
的问题的答案,但我会与你分享我的想法(post 的第二部分)
您是否考虑过使用 Interceptor
?
它们提供了在您进入控制器之前以及在执行 @ControllerAdvice
之后(如果有的话)记录操作的方法。
@Component
public class WebInterceptor extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(WebInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.error("WebInterceptor prehandle is now logged");
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.error("WebInterceptor after completion is now logged");
super.afterCompletion(request, response, handler, ex);
}
}
要激活此拦截器,您需要创建一个新的 @Configuration
class,实现 WebMvcConfigurer
:
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Autowired
private WebInterceptor webInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webInterceptor).addPathPatterns("/user");
}
}
我创建了一个简单的 Controller
和 ControllerAdvice
用于测试:
@RestController
public class WebController {
@GetMapping("/user")
public String user() {
throw new IllegalArgumentException("my bad");
}
}
@RestControllerAdvice
@Order(2)
public class WebAdvice {
private Logger logger = LoggerFactory.getLogger(WebAdvice.class);
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handle(RuntimeException e) {
logger.error("ControllerAdvice is now logged");
}
}
结果是:
WebAdvice handle is now logged
WebInterceptor after completion is now logged
如果你需要记录请求,你也可以看看这个:https://www.baeldung.com/spring-http-logging
这是我发现的有关 Order、Aspect 和 ControllerAdvice 的内容
你是对的,@ControllerAdvice
和 @Aspect
之间没有优先级,有点...
我找不到原因(还没有),但是当 @Aspect
指向 @Controller
方法时,切点将在 @ControllerAdvice
之前执行, 无论您设置的顺序如何
@Aspect
@Component
@Order(1)
public class MyAspect {
private Logger logger = LoggerFactory.getLogger(MyAspect.class);
@After("execution(* WebController.user(..))")
public void afterLog() {
logger.error("Aspect is now logged");
}
}
@RestControllerAdvice
@Order(2)
public class WebAdvice {
private Logger logger = LoggerFactory.getLogger(WebAdvice.class);
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handle(RuntimeException e) {
logger.error("ControllerAdvice is now logged");
}
}
如果您 Get http://localhost:8080/user
结果将是:
Aspect is now logged
ControllerAdvice is now logged
但我发现了一些有趣的事情:如果你设置切入点 @After
和 @ControllerAdvice
那么你就会在 controllerAdvice:
之后执行方面
@Aspect
@Component
@Order(1)
public class MyAspect {
private Logger logger = LoggerFactory.getLogger(MyAspect.class);
@After("execution(* WebAdvice.handle(..)) || execution(* WebController.user(..))")
public void afterLog() {
logger.error("Aspect is now logged");
}
}
执行了2次。一个在 Controller 之后,一个在 Advice 之后。
Aspect is now logged
ControllerAdvice is now logged
Aspect is now logged
也许这可以满足您的需求,但我并不认为这是一个优雅的解决方案...
尚待解决的问题:
- 寻找一种优先于
Aspect
和 ControllerAdvice
的方法
希望对您有所帮助。如果我找到更好的东西,我会告诉你的。
我有一个使用 @Aspect
的自定义记录器,我希望它最后总是 运行 这样无论从控制器返回什么响应,它都会被记录到数据库中(所以我在这方面加上 @Order(1)
)。我还使用 @ControllerAdvice
编写了一个错误处理程序来处理所有意外异常,并使用 returns 500
和自定义响应主体编写了一个错误处理程序,我希望它也被记录器记录下来,所以我在上面放一个 @Order(2)
,但是看起来放 @Order
注释并没有安排 Spring Aspect 和 Spring ControllerAdvice 之间的顺序,那么如何让我的错误处理程序始终 运行 在记录器之前? (当然,没有将我的错误处理程序转到另一个 Spring 方面)
我一直在寻找和调试您的主要问题。我没有找到你关于 @Order
的问题的答案,但我会与你分享我的想法(post 的第二部分)
您是否考虑过使用 Interceptor
?
它们提供了在您进入控制器之前以及在执行 @ControllerAdvice
之后(如果有的话)记录操作的方法。
@Component
public class WebInterceptor extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(WebInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.error("WebInterceptor prehandle is now logged");
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.error("WebInterceptor after completion is now logged");
super.afterCompletion(request, response, handler, ex);
}
}
要激活此拦截器,您需要创建一个新的 @Configuration
class,实现 WebMvcConfigurer
:
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Autowired
private WebInterceptor webInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webInterceptor).addPathPatterns("/user");
}
}
我创建了一个简单的 Controller
和 ControllerAdvice
用于测试:
@RestController
public class WebController {
@GetMapping("/user")
public String user() {
throw new IllegalArgumentException("my bad");
}
}
@RestControllerAdvice
@Order(2)
public class WebAdvice {
private Logger logger = LoggerFactory.getLogger(WebAdvice.class);
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handle(RuntimeException e) {
logger.error("ControllerAdvice is now logged");
}
}
结果是:
WebAdvice handle is now logged
WebInterceptor after completion is now logged
如果你需要记录请求,你也可以看看这个:https://www.baeldung.com/spring-http-logging
这是我发现的有关 Order、Aspect 和 ControllerAdvice 的内容
你是对的,@ControllerAdvice
和 @Aspect
之间没有优先级,有点...
我找不到原因(还没有),但是当 @Aspect
指向 @Controller
方法时,切点将在 @ControllerAdvice
之前执行, 无论您设置的顺序如何
@Aspect
@Component
@Order(1)
public class MyAspect {
private Logger logger = LoggerFactory.getLogger(MyAspect.class);
@After("execution(* WebController.user(..))")
public void afterLog() {
logger.error("Aspect is now logged");
}
}
@RestControllerAdvice
@Order(2)
public class WebAdvice {
private Logger logger = LoggerFactory.getLogger(WebAdvice.class);
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handle(RuntimeException e) {
logger.error("ControllerAdvice is now logged");
}
}
如果您 Get http://localhost:8080/user
结果将是:
Aspect is now logged
ControllerAdvice is now logged
但我发现了一些有趣的事情:如果你设置切入点 @After
和 @ControllerAdvice
那么你就会在 controllerAdvice:
@Aspect
@Component
@Order(1)
public class MyAspect {
private Logger logger = LoggerFactory.getLogger(MyAspect.class);
@After("execution(* WebAdvice.handle(..)) || execution(* WebController.user(..))")
public void afterLog() {
logger.error("Aspect is now logged");
}
}
执行了2次。一个在 Controller 之后,一个在 Advice 之后。
Aspect is now logged
ControllerAdvice is now logged
Aspect is now logged
也许这可以满足您的需求,但我并不认为这是一个优雅的解决方案...
尚待解决的问题:
- 寻找一种优先于
Aspect
和ControllerAdvice
的方法
希望对您有所帮助。如果我找到更好的东西,我会告诉你的。