Como escrever um manipulador de erro global adequado com o Spring MVC / Spring Boot

Estou escrevendo um aplicativo da Web com o Spring 4.0.4 e o Spring Boot 1.0.2 usando o Tomcat como contêiner de Web incorporado e desejo implementar um tratamento de exceção global que intercepta todas as exceções e registra-as de uma maneira específica. Minhas exigências simples são:

  • Eu quero manipular globalmente todas as exceções que ainda não são processadas em outro lugar (em um manipulador de exceção do controlador, por exemplo). Eu quero registrar a mensagem e quero exibir uma mensagem de erro personalizada para o usuário.
  • Não quero que o Spring ou o contêiner da web registrem erros por si só, porque quero fazer isso sozinho.

Até agora, minha solução é assim (simplificada, sem registro e sem redirecionamento para uma exibição de erro):

@Controller @RequestMapping("/errors") public class ErrorHandler implements EmbeddedServletContainerCustomizer { @Override public void customize(final ConfigurableEmbeddedServletContainer factory) { factory.addErrorPages(new ErrorPage("/errors/unexpected")); factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/notfound")); } @RequestMapping("unexpected") @ResponseBody public String unexpectedError(final HttpServletRequest request) { return "Exception: " + request.getAttribute("javax.servlet.error.exception"); } @RequestMapping("notfound") @ResponseBody public String notFound() { return "Error 404"; } } 

O resultado é que as exceções lançadas nos controladores são manipuladas corretamente pelo método unexpectedError e os códigos de status 404 são manipulados pelo método notFound . Até aí tudo bem, mas tenho os seguintes problemas:

  • Tomcat ou Spring (não sei quem é o responsável) ainda está registrando a mensagem de erro. Eu não quero isso porque eu quero fazer o log sozinho (com informações adicionais) e não quero mensagens de erro duplicadas no log. Como posso evitar esse registro padrão?
  • A maneira que eu access o object de exceção não parece certo. Eu busco se do atributo de solicitação javax.servlet.error.exception . E essa não é a exceção gerada, é uma instância de org.springframework.web.util.NestedServletException e eu tenho que mergulhar nessa exceção aninhada para buscar a real. Tenho certeza que existe um jeito mais fácil, mas não consigo encontrá-lo.

Então, como posso resolver esses problemas? Ou talvez a maneira como implementei esse manipulador global de exceções esteja completamente errada e haja uma alternativa melhor?

Dê uma olhada no ControllerAdvice Você poderia fazer algo assim:

 @ControllerAdvice public class ExceptionHandlerController { public static final String DEFAULT_ERROR_VIEW = "error"; @ExceptionHandler(value = {Exception.class, RuntimeException.class}) public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) { ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW); mav.addObject("datetime", new Date()); mav.addObject("exception", e); mav.addObject("url", request.getRequestURL()); return mav; } }