Spring MVC
In this example, we will replace Spring default HandlerExceptionResolvers with custom one.
Prerequisite
Ordering and customization of default
HandlerExceptionResolvers
Steps
- 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. - 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).
- In @EnableWebMvc class, register the custom HandlerExceptionResolver with name DispatcherServlet#HANDLER_EXCEPTION_RESOLVER_BEAN_NAME.
- Create default-error.jsp
- 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
|