如何让Spring@ControllerAdise与其他定制的Spring@Aspects一起工作?

2022-04-16 00:00:00 spring aop java spring-boot spring-aop

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


解决方案

我一直在寻找并调试您的主要问题。我没有找到你关于@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);
    }
}

要激活此侦听器,您需要创建一个新的@Configuration类,实现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");
    }
}

结果为:

现在已记录WebAdise句柄
现在已记录完成后的WebInterceptor

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

以下是我找到的有关Order、Aspect和ControllerAdance的信息

你说得对,@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,结果将为:

方面现在已记录
ControllerAdise现在已记录

但我发现了一些有趣的事情:如果您设置了一个切入点@After@ControllerAdvice,那么您的方面将在ControlerAdance:

之后执行
@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次。一个在控制器之后,另一个在建议之后。

方面现在已记录
ControllerAdise现在已记录
Aspects现在已记录

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

仍需解决的事情:

  • 找到优先于AspectControllerAdvice的方法

希望不管怎样,它都会有所帮助。如果我找到更好的,我会让你知道的。

相关文章