Close

Replacing Spring default HandlerExceptionResolvers

[Last Updated: Dec 7, 2017]

Spring MVC 

In this example, we will replace Spring default HandlerExceptionResolvers with custom one.

Prerequisite

Ordering and customization of default HandlerExceptionResolvers

Steps

  1. In WebApplicationInitializer, we will set detectAllHandlerExceptionResolvers property of DispatcherServlet to false, doing so will register just one single bean of type HandlerExceptionResolver and will replace the default one.
  2. Create a custom HandlerExceptionResolverComposite which will add ExceptionHandlerExceptionResolver and SimpleMappingExceptionResolver in it's list. The desired functionality, in exception scenario, would be: if @ExceptionHandler method is defined for an exception then that will process the exception, otherwise a default page will be used (set by SimpleMappingExceptionResolver).
  3. In @EnableWebMvc class, register the custom HandlerExceptionResolver with name DispatcherServlet#HANDLER_EXCEPTION_RESOLVER_BEAN_NAME.
  4. Create default-error.jsp
  5. Create a controller class.


WebApplicationInitializer implementation

public class AppInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses () {
      return new Class<?>[]{AppConfig.class};
  }

  @Override
  protected Class<?>[] getServletConfigClasses () {
      return null;
  }

  @Override
  protected String[] getServletMappings () {
      return new String[]{"/"};
  }

  @Override
  protected FrameworkServlet createDispatcherServlet (WebApplicationContext wac) {
      DispatcherServlet ds = new DispatcherServlet(wac);
      ds.setDetectAllHandlerExceptionResolvers(false);
      return ds;
  }
}

Creating a custom HandlerExceptionResolverComposite and registering as a bean

@EnableWebMvc
@ComponentScan("com.logicbig.example")
public class AppConfig {
  
  @Bean(DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME)
  HandlerExceptionResolver customExceptionResolver (ApplicationContext ac) {

      ExceptionHandlerExceptionResolver e = new ExceptionHandlerExceptionResolver();
      e.setApplicationContext(ac);
      e.afterPropertiesSet();

      SimpleMappingExceptionResolver s = new SimpleMappingExceptionResolver();
      s.setDefaultErrorView("default-error");
      s.setDefaultStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());

      HandlerExceptionResolverComposite c = new HandlerExceptionResolverComposite();
      c.setExceptionResolvers(Arrays.asList(e, s));
      return c;
  }
  
  @Bean
  public ViewResolver viewResolver () {
      InternalResourceViewResolver viewResolver =
                new InternalResourceViewResolver();
      viewResolver.setPrefix("/WEB-INF/views/");
      viewResolver.setSuffix(".jsp");
      return viewResolver;
  }
}

src/main/webapp/WEB-INF/views/default-error.jsp

D:\LogicBig\example-projects\spring-mvc\replacing-default-exception-resolvers\src\main\webapp\WEB-INF\views\default-error.jsp

The Controller

@Controller
public class ExampleController {

  @RequestMapping("/test")
  public String handleRequest () throws Exception {
      throw new IllegalAccessException();
  }

  /**
   * This will throw MissingPathVariableException with response code 500
   * because id != testId
   */
  @RequestMapping("/test/{id}")
  @ResponseBody
  public String handleRequest2 (@PathVariable("testId") String id) throws Exception {
      return "testId: " + id;
  }

  @RequestMapping("/test2")
  public void handleRequest3 () throws Exception {
      throw new Exception("test exception 2");
  }

  @ExceptionHandler
  @ResponseBody
  public String handleException (IllegalAccessException b) {
      return "from @ExceptionHandler: " + b;
  }
}

Run embedded tomcat

mvn tomcat7:run-war

output

URI: /test/{id}

This should throw MissingPathVariableException exception which will map to the default-error.jsp.


URI: /test

This should invoke handleException(..) method of our controller. This method is annotated with @ExceptionHandler and has parameter of the matching exception.

This shows that ExceptionHandlerExceptionResolver has been processed as expected.


URI: /test2

The exception thrown in the corresponding controller is not handled by any @ExceptionHandler, so default-error.jsp will be processed:


Other URIs

Other uri, would normally, return 404 error:

If we want all 404 default page should be routed to default-error.jsp as well, then we have to set the following in our AppInitializer.java
DispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
Check out the related example here.




Other way to replace default resolvers

In this example, we saw how to register just one exception resolver by setting DispatcherServlet#detectAllHandlerExceptionResolvers=false and registering just one HandlerExceptionResolve by name DispatcherServlet#HANDLER_EXCEPTION_RESOLVER_BEAN_NAME.

Alternatively, we can define a HandlerExceptionResolver with name 'handlerExceptionResolver' without turning off the above flag of DispatcherServlet. That's because this is the bean name which is configured by WebMvcConfigurationSupport#handlerExceptionResolver() for the default exception resolvers. This way, we can register more than one HandlerExceptionResolvers and replace the default ones as well.



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

replacing-default-exception-resolvers Select All Download
  • replacing-default-exception-resolvers
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • AppConfig.java
          • webapp
            • WEB-INF
              • views

    See Also