如何让 Spring @ControllerAdvice 与其他自定义 Spring @Aspect 一起工作?

Che*_*hen 6 java aop spring spring-aop spring-boot

我有一个自定义记录器@Aspect,我希望它始终在最后运行,这样无论控制器返回什么响应,它都会被记录到数据库中(所以我@Order(1)在这方面添加了一个)。我还编写了一个错误处理程序,用于@ControllerAdvice处理所有意外异常并返回500自定义响应正文,并且我希望记录器也记录它,因此我@Order(2)在其上添加了一个,但是看起来放置@Order注释并没有安排Spring Aspect 和 Spring ControllerAdvice 之间的命令,那么如何让我的错误处理程序始终在记录器之前运行?(当然,无需将我的错误处理程序转换为另一个 Spring Aspect)

RUA*_*ult 7

我一直在寻找和调试您的主要问题。我没有找到你的问题的答案@Order,但我会与你分享我的想法(这篇文章的第二部分)

您考虑过使用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);
    }
}

Run Code Online (Sandbox Code Playgroud)

要激活此拦截器,您需要创建一个新@Configuration类,实现WebMvcConfigurer

@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Autowired
    private WebInterceptor webInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(webInterceptor).addPathPatterns("/user");
    }
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个简单的ControllerandControllerAdvice进行测试:

@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

现在记录 WebAdvice 句柄 现在
记录完成后的 WebInterceptor

如果您需要记录请求,您也可以查看此内容: https: //www.baeldung.com/spring-http-logging

这是我发现的有关 Order、Aspect 和 ControllerAdvice 的内容

@ControllerAdvice你是对的, an和 a之间没有优先级@Aspect,嗯……

我无法找出原因(还没有),但是当@Aspectin 指向方法时,无论您设置的顺序如何,@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你的Get http://localhost:8080/user结果将是:

方面现已记录
ControllerAdvice 现已记录

但我发现了一些有趣的事情:如果你设置了一个切入点@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

执行2次。一份在控制器之后,一份在建议之后。

方面现已记录
ControllerAdvice 现已记录
方面现已记录

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

还需要解决的事情:

  • 寻找一种优先于Aspect和 的方法ControllerAdvice

无论如何希望它有帮助。如果我找到更好的东西,我会告诉你。