Close

Spring MVC - Using multiple HandlerExceptionResolvers together

[Last Updated: Jan 25, 2017]

We can use multiple HandlerExceptionResolver at a time by using HandlerExceptionResolverComposite.

java.lang.ObjectObjectorg.springframework.web.servlet.handler.HandlerExceptionResolverCompositeHandlerExceptionResolverCompositeorg.springframework.web.servlet.HandlerExceptionResolverHandlerExceptionResolverorg.springframework.core.OrderedOrderedLogicBig

Following method of HandlerExceptionResolverComposite can be used to specify multiple HandlerExceptionResolvers:

public void setExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers)

In resolveException(...) method, this resolver loops through the provided list of resolvers. The first one to return a ModelAndView instance wins. Otherwise null is returned. Here's the snippet:

public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {

 private List<HandlerExceptionResolver> resolvers;
 ....................
 public void setExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.resolvers = exceptionResolvers;
 }
 ..................
  @Override
  public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response,
            Object handler,
            Exception ex) {
   if (resolvers != null) {
    for (HandlerExceptionResolver handlerExceptionResolver : resolvers) {
       ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
       if (mav != null) {
        return mav;
       }
    }
   }
   return null;
  }
 }

Note that, if any of the the provided HandlerExceptionResolver, implements Ordered interface, then the order returned from Ordered#getOrder is not used to sort the provided resolvers. The resolvers are iterated in list's insertion order. So what is the use of Ordered#getOrder()? It is actually used in the DispatcherServlet to iterate through the default and registered exception resolvers. (in the next tutorial we will look into that with an example).

In following example we are going to set two HandlerExceptionResolvers with HandlerExceptionResolverComposite, one from our last tutorial example which is a custom resolver and other will be SimpleMappingExceptionResolver.


Example

Registering HandlerExceptionResolvers in JavaConfig class

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

    @Bean
    HandlerExceptionResolver exceptionResolverComposite () {
        //the custom resolver from last example
        HandlerExceptionToViewResolver r = new HandlerExceptionToViewResolver();

        SimpleMappingExceptionResolver s = new SimpleMappingExceptionResolver();
        s.setDefaultErrorView("default-error-page");
        Properties p = new Properties();
        p.setProperty(IllegalAccessException.class.getName(), "simple-test-error-view");
        s.setExceptionMappings(p);

        HandlerExceptionResolverComposite c = new HandlerExceptionResolverComposite();
        c.setExceptionResolvers(Arrays.asList(r, s));
        return c;
    }
   .......
}

The Controller

@Controller
public class ExampleController {

    //should be handled by our custom resolver because of @ErrorView
    @ErrorView(value = "test-error-view", status = HttpStatus.GONE)
    @RequestMapping("/test1")
    public String handleRequest () throws Exception {
        throw new Exception("test exception 1");
    }

    //should be handled by handleException() because of @ExceptionHandler.
    //Default resolvers are processed before application registered ones
    @RequestMapping("/test2")
    public String handleRequest2 () throws Exception {
        throw new OperationNotSupportedException("test exception 2");
    }

    //should be handled by SimpleMappingExceptionResolver's default view
    @RequestMapping("/test3")
    public String handleRequest3 () throws Exception {
        throw new Exception("test exception 3");
    }

    //should be handled by SimpleMappingExceptionResolver:
    //IllegalAccessException is mapped to 'test-error-view'
    @RequestMapping("/test4")
    public String handleRequest4 () throws Exception {
        throw new IllegalAccessException("test exception 4");
    }

    //should be handled by our custom resolver even though IllegalAccessException
    //is mapped in SimpleMappingExceptionResolver. Reasons is: in iteration order
    // our custom resolver is first to process
    @ErrorView(value = "test-error-view", status = HttpStatus.FORBIDDEN)
    @RequestMapping("/test5")
    public String handleRequest5 () throws Exception {
        throw new IllegalAccessException("test exception 5");
    }

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

JSP pages

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>

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

<html>
<body>
<h3>Simple Test Error View</h3>
 <p>Exception: <b>${exception['class'].name}</b></p>
 <p>Message: <b>${exception.message}</b></p>
</body>
</html>
src/main/webapp/WEB-INF/views/default-error-page.jsp
<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>

<html>
<body>
<h3>This is the default exception page</h3>
 <p>Exception: <b>${exception['class'].name}</b></p>
 <p>Message: <b>${exception.message}</b></p>
</body>
</html>

Run embedded tomcat

mvn tomcat7:run-war

output

/test1


/test2


/test3


/test4


/test5


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

Handler Exception Resolver Composite Select All Download
  • handler-exception-resolver-composite
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • AppConfig.java
          • webapp
            • WEB-INF
              • views

    See Also