Spring MVC - Creating a custom HandlerExceptionResolver

[Updated: Jul 10, 2017, Created: Jan 20, 2017]

We have seen the basic concept and a simple usage of low level HandlerExceptionResolver interface.

In this tutorial we are going to demonstrate how to create a custom HandlerExceptionResolver to introduce new ways to handle exceptions in Spring MVC.


Example

steps:

  1. Create a new annotation ErrorView, which will specify a view name and status code.
  2. Create a custom HandlerExceptionResolver implementation, HandlerExceptionToViewResolver, whose method resolveException() will find out whether the source handler method (where exception occurred) has annotation ErrorView. If yes, it will get the view name and status code from the annotation and after setting the specified status code to the response, it will return the specified error page. If the handler method doesn't have ErrorView annotation the default error processing will continue.
  3. Create a controller and a jsp page for testing.

Creating custom annotation ErrorView

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ErrorView {

    String value ();

    HttpStatus status () default HttpStatus.INTERNAL_SERVER_ERROR;
}

Creating custom HandlerExceptionResolver

public class HandlerExceptionToViewResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException (HttpServletRequest request,
                                          HttpServletResponse response,
                                          Object handler,
                                          Exception ex) {

        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            ErrorView errorView = hm.getMethodAnnotation(ErrorView.class);
            if (errorView != null) {
                //preparing ModelAndView
                String viewName = errorView.value();
                ModelAndView model = new ModelAndView(viewName);
                model.addObject("requestUri", request.getRequestURI());
                model.addObject("exception", ex);

                HttpStatus status = errorView.status();
                model.addObject("statusValue", status.value());
                model.addObject("statusStr", status.getReasonPhrase());
                //setting status code
                response.setStatus(status.value());

                return model;
            }
        }
        //returning null for default processing
        return null;
    }
}

Registering custom HandlerExceptionResolver

@EnableWebMvc
@ComponentScan("com.logicbig.example")
public class AppConfig {

    @Bean
    HandlerExceptionResolver customExceptionResolver () {
        return new HandlerExceptionToViewResolver();
    }
    ......
}

Creating the Controller

In the following controller we are creating two handler methods, first one with @ErrorView and second one without it. The second one throws an exception which should map to @ExceptionHandler method (the third method), we are doing that to make sure that the default processing is not broken.

@Controller
public class ExampleController {

    @ErrorView(value = "test-error-view", status = HttpStatus.GONE)
    @RequestMapping("/test")
    public String handleRequest () throws Exception {
        throw new Exception("test exception");
    }

    @RequestMapping("/test2")
    public String handleRequest2 () throws Exception {
        throw new OperationNotSupportedException("exception thrown in test2");
    }

    @ExceptionHandler
    @ResponseBody
    public String handleException (OperationNotSupportedException e) {
        return "exception :" + e.toString();
    }
}

JSP error page

src/main/webapp/WEB-INF/views/test-error-view.jsp
<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>

<html>
<body>
<h3>Test Error View</h3>
 <p>Request Uri: <b>${requestUri}</b></p>
 <p>Exception: <b>${exception['class'].name}</b></p>
 <p>Message: <b>${exception.message}</b></p>
 <p>Response status: <b>${statusValue} (${statusStr})</b></p>
</body>
</html>


Running embedded tomcat

mvn tomcat7:run-war

Output

/test

Confirming status code in HTTPie:


/test2



Example Project

Dependencies and Technologies Used :

  • spring-webmvc 4.3.5.RELEASE: Spring Web MVC.
  • javax.servlet-api 3.0.1 Java Servlet API
  • JDK 1.8
  • Maven 3.3.9

Custom Handler Exception Resolver Select All Download
  • custom-handler-exception-resolver
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • webapp
          • WEB-INF
            • views

See Also