如何让 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");
    }
}

我创建了一个简单的 ControllerControllerAdvice 用于测试:

@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

也许这可以满足您的需求,但我并不认为这是一个优雅的解决方案...

尚待解决的问题:

  • 寻找一种优先于 AspectControllerAdvice
  • 的方法

希望对您有所帮助。如果我找到更好的东西,我会告诉你的。