如何使Spring @ControllerAdvice与其他自定义Spring @Aspect一起使用?

问题描述 投票:0回答:1

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

java spring spring-boot aop spring-aop
1个回答
0
投票

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

结果是:

现在已记录WebAdvice句柄完成后现在记录WebInterceptor]

如果您需要记录请求,也可以查看一下: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,结果将是:

现在已记录方面现在已记录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");
    }
}

执行两次。一处在管制员之后,一处在建议之后。

现在已记录方面现在已记录ControllerAdvice现在已记录方面[]

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

仍需解决的问题:

  • 找到优先于AspectControllerAdvice的方法
  • 希望无论如何都会有帮助。如果我发现更好的地方,我会通知您。

© www.soinside.com 2019 - 2024. All rights reserved.