Java 当找不到匹配的处理程序时,如何调用SpringMVC拦截器

Java 当找不到匹配的处理程序时,如何调用SpringMVC拦截器,java,spring,spring-boot,interceptor,exceptionhandler,Java,Spring,Spring Boot,Interceptor,Exceptionhandler,我有一个简单的Spring Boot 2.1应用程序,带有Spring拦截器和@RestControllerAdvice 我的要求是在所有情况下调用Spring拦截器,包括发生异常时 对于自定义异常,会调用拦截器处理程序方法,例如preHandle()和afterCompletion()。但是,对于由ResponseEntityExceptionHandler处理的异常,不会调用Spring拦截器(我需要ResponseEntityExceptionHandler的方法来创建一个自定义respo

我有一个简单的Spring Boot 2.1应用程序,带有Spring拦截器和
@RestControllerAdvice

我的要求是在所有情况下调用Spring拦截器,包括发生异常时

对于自定义异常,会调用拦截器处理程序方法,例如
preHandle()
afterCompletion()
。但是,对于由
ResponseEntityExceptionHandler
处理的异常,不会调用Spring拦截器(我需要
ResponseEntityExceptionHandler
的方法来创建一个自定义
responseBy
以发回,但是,我还需要触发拦截器的
完成后()
用于审计目的)

例如,如果使用
补丁
HTTP方法发出REST请求,则它只执行
PersonControllerExceptionHandler.handleHttpRequestMethodNotSupported()
,并且不调用
PersonInterceptor

异常处理程序

@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonControllerExceptionHandler extends ResponseEntityExceptionHandler {

    private final static Logger LOGGER = LoggerFactory.getLogger(PersonControllerExceptionHandler.class);

    @ExceptionHandler(value = {PersonException.class })
    public ResponseEntity<Object> handlePersonException(PersonException exception) {
        LOGGER.info("Person exception occurred");

        return new ResponseEntity<Object>(new Person("Bad Age", -1),
                HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(value = {Exception.class })
    public ResponseEntity<Object> handleException(Exception exception) {
        LOGGER.info("Exception occurred");

        return new ResponseEntity<Object>(new Person("Unknown Age", -100),
                HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @Override
    public ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
                                                                      HttpHeaders headers,
                                                                      HttpStatus status,
                                                                      WebRequest request) {
        LOGGER.info("handleHttpRequestMethodNotSupported()...");
        return new ResponseEntity<>(new Person("Argh!", 900), HttpStatus.METHOD_NOT_ALLOWED);
    }

}
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(PersonInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        LOGGER.info("PersonInterceptor#preHandler()...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        LOGGER.info("PersonInterceptor#postHandler()...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        LOGGER.info("PersonInterceptor#afterCompletion()...");

        if (ex != null) {
            LOGGER.error("afterCompletion(): An exception occurred", ex);
        }
    }
}
@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PersonInterceptor()).addPathPatterns("/person/*");
    }
}
@RestController
@RequestMapping("/")
public class PersonController {

    private final static Logger LOGGER = LoggerFactory.getLogger(PersonController.class);

    @Autowired
    private PersonService personService;

    @GetMapping(path = "/person/{age}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Person getPerson(@PathVariable("age") Integer age) throws PersonException {
        LOGGER.info("Age: {}", age);

        return personService.getPerson(age);
    }
}
注册拦截器

@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonControllerExceptionHandler extends ResponseEntityExceptionHandler {

    private final static Logger LOGGER = LoggerFactory.getLogger(PersonControllerExceptionHandler.class);

    @ExceptionHandler(value = {PersonException.class })
    public ResponseEntity<Object> handlePersonException(PersonException exception) {
        LOGGER.info("Person exception occurred");

        return new ResponseEntity<Object>(new Person("Bad Age", -1),
                HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(value = {Exception.class })
    public ResponseEntity<Object> handleException(Exception exception) {
        LOGGER.info("Exception occurred");

        return new ResponseEntity<Object>(new Person("Unknown Age", -100),
                HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @Override
    public ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
                                                                      HttpHeaders headers,
                                                                      HttpStatus status,
                                                                      WebRequest request) {
        LOGGER.info("handleHttpRequestMethodNotSupported()...");
        return new ResponseEntity<>(new Person("Argh!", 900), HttpStatus.METHOD_NOT_ALLOWED);
    }

}
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(PersonInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        LOGGER.info("PersonInterceptor#preHandler()...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        LOGGER.info("PersonInterceptor#postHandler()...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        LOGGER.info("PersonInterceptor#afterCompletion()...");

        if (ex != null) {
            LOGGER.error("afterCompletion(): An exception occurred", ex);
        }
    }
}
@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PersonInterceptor()).addPathPatterns("/person/*");
    }
}
@RestController
@RequestMapping("/")
public class PersonController {

    private final static Logger LOGGER = LoggerFactory.getLogger(PersonController.class);

    @Autowired
    private PersonService personService;

    @GetMapping(path = "/person/{age}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Person getPerson(@PathVariable("age") Integer age) throws PersonException {
        LOGGER.info("Age: {}", age);

        return personService.getPerson(age);
    }
}
控制器

@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonControllerExceptionHandler extends ResponseEntityExceptionHandler {

    private final static Logger LOGGER = LoggerFactory.getLogger(PersonControllerExceptionHandler.class);

    @ExceptionHandler(value = {PersonException.class })
    public ResponseEntity<Object> handlePersonException(PersonException exception) {
        LOGGER.info("Person exception occurred");

        return new ResponseEntity<Object>(new Person("Bad Age", -1),
                HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(value = {Exception.class })
    public ResponseEntity<Object> handleException(Exception exception) {
        LOGGER.info("Exception occurred");

        return new ResponseEntity<Object>(new Person("Unknown Age", -100),
                HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @Override
    public ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
                                                                      HttpHeaders headers,
                                                                      HttpStatus status,
                                                                      WebRequest request) {
        LOGGER.info("handleHttpRequestMethodNotSupported()...");
        return new ResponseEntity<>(new Person("Argh!", 900), HttpStatus.METHOD_NOT_ALLOWED);
    }

}
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(PersonInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        LOGGER.info("PersonInterceptor#preHandler()...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        LOGGER.info("PersonInterceptor#postHandler()...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        LOGGER.info("PersonInterceptor#afterCompletion()...");

        if (ex != null) {
            LOGGER.error("afterCompletion(): An exception occurred", ex);
        }
    }
}
@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PersonInterceptor()).addPathPatterns("/person/*");
    }
}
@RestController
@RequestMapping("/")
public class PersonController {

    private final static Logger LOGGER = LoggerFactory.getLogger(PersonController.class);

    @Autowired
    private PersonService personService;

    @GetMapping(path = "/person/{age}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Person getPerson(@PathVariable("age") Integer age) throws PersonException {
        LOGGER.info("Age: {}", age);

        return personService.getPerson(age);
    }
}
起初我认为这与
@Ordered
有关,但尝试各种场景时,我给
PersonInterceptor
一个比
@RestControllerAdvice
更高的优先级会产生同样的不良结果(反之亦然)

在深入研究Spring框架之后,似乎如果找不到处理程序,就会将异常抛出回
DispatcherServlet#doDispatch()
,该异常进入
catch
块,因此,它会跳过拦截器映射过程,包括
afterCompletion()
(我使用
Spring5.1
。作为跟踪执行路径的示例):

  • 调用并尝试获取
    HandlerExecutionChain
  • 我可以看到有几个
    HandlerMapping
    ;失败的是
    RequestMappingHandlerMapping
  • 在中,它试图通过
  • 最终调用,由于没有
    修补程序getPerson()
    ,而是
    获取getPerson()
    ,无法找到匹配的模式
  • 此时,抛出HttpRequestMethodNotSupportedException
  • 这个异常冒泡到exception子句,然后由它在中找到的异常解析器进行处理 (当然,这会找到一个异常解析程序,而不会引发异常,当异常在exception子句中被捕获时,可能会触发该异常。)

  • 在没有处理程序匹配的情况下,我是否缺少触发拦截器的
    afterCompletion()

    这是一个非常古老且非常破碎的spring功能,既不是RestControllerAdvice,也不是ControllerAdvice或ErrorController(已弃用)当涉及404处理时,按照预期工作。遗憾的是,这一直是这个标志性的spring错误。谢谢。您建议在spring框架中使用什么来处理全局和容器错误呢?我不认为有人反对。