Hello,
In this article, I would present you the exception handling and resolving of Spring MVC in a web application. Usually, it’s recommended to display a error page instead of the default long java plain exception code in web application. So, it is possible to configure a custom error page to map a specified error code or exception type in the classic web.xml of servlet container:
<error-page> <error-code>404</error-code> <location>/WEB-INF/jsp/my404.jsp</location> </error-page> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/WEB-INF/jsp/my404.jsp</location> </error-page>
However, the Spring framework proposes a resolving system of exception, relative to the previous system of the servlet container, via the data population because Spring allows the population of model for the error page with specific data: title, message…etc.
But, often, it could be necessary to apply extra business logic like sending an email to administrators, logging, auditing before render the error page when an exception is thrown from business layer. So, in this tutorial, we will extend the class provided by Spring SimpleMappingExceptionResolver to do the exception handling in Spring MVC application.
1. Custom exceptions
Create two simple customized exceptions:
package com.ho.exceptionresolver; //... public class MyBusinessException extends Exception { public MyBusinessException() { } public MyBusinessException(String message) { super(message); } public MyBusinessException(Throwable cause) { super(cause); } public MyBusinessException(String message, Throwable cause) { super(message, cause); } // ... }
and
package com.ho.exceptionresolver; //... public class MyTechnicalException extends Exception { public MyTechnicalException() { } public MyTechnicalException(String message) { super(message); } public MyTechnicalException(Throwable cause) { super(cause); } public MyTechnicalException(String message, Throwable cause) { super(message, cause); } // ... }
2. Presentation Layer
For our tutorial :), we will throw a MyBusinessException exception and a MyTechnicalException exception in the controller class MyController:
package com.ho.exceptionresolver; //... public class MyController extends MultiActionController{ public ModelAndView handleRequest1(HttpServletRequest request, HttpServletResponse response) throws Exception { //... throw new MyBusinessException("Heyyy...a business exception message"); //... } public ModelAndView handleRequest2(HttpServletRequest request, HttpServletResponse response) throws Exception { //... throw new MyTechnicalException("Heyyy...a technical exception message"); //... } // ... }
3. Extend SimpleMappingExceptionResolver
Then, we will create a custom class named CustomSimpleMappingExceptionResolver extending the Spring class SimpleMappingExceptionResolver and overrides its doResolveException method. The goals of this class will be:
- get all exceptions (+customization) thrown in the Spring controllers,
- set the specific error parameters (“errorTitle” and “errorMessage”) depending of the exception type,
In the doResolveException method, we could apply extra business logic (auditing, logging or sending emails) depending on type of exception MyBusinessException, MyTechnicalException and Exception.
package com.ho.exceptionresolver; import org.springframework.web.servlet.ModelAndView; import java.lang.Exception; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.Object; import com.ho.exceptionresolver.MyBusinessException; public class CustomSimpleMappingExceptionResolver extends SimpleMappingExceptionResolver{ //-------------------------------- CONSTRUCTOR public CustomSimpleMappingExceptionResolver (){ super(); } //-------------------------------- PROTECTED METHODS @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){ ModelAndView modelAndView = super.doResolveException(request, response, handler, ex); String errorTitle = ""; if(ex instanceof MyBusinessException){ errorTitle = "A specific title for the MyBusinessException exception"; // ... Apply extra business logic: auditing, logging // ...... for the MyBusinessException exception }else if(ex instanceof MyTechnicalException){ errorTitle = "A specific title for the MyTechnicalException exception"; // ... Apply extra business logic: sending emails // ...... for the MyTechnicalException exception }else{ errorTitle = "Error standard exception"; } modelAndView.getModelMap().addAttibute("errorTitle", errorTitle ); modelAndView.getModelMap().addAttibute("errorMessage", ex.getMessage()); return modelAndView; } // ... }
4. Spring Configuration
In Spring’s bean configuration file, we will declare a InternalResourceViewResolver and the CustomSimpleMappingExceptionResolver class which maps:
- the “MyBusinessException” exception to the view name “businessErrorPage” for the “/WEB-INF/jsp/businessErrorPage.jsp”,
- the “MyTechnicalException” exception to the view name “technicalErrorPage” for the “/WEB-INF/jsp/technicalErrorPage.jsp” page,
- any Exception or its sub class to the view name “rrorPage” for the “/WEB-INF/jsp/errorPage.jsp” page,
<!-- ################### SPRING MVC VIEW RESOLVER ################### --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> <property name="order"><value>2</value></property> </bean> <!-- ################### EXCEPTION RESOLVER ################### --> <bean name="myExceptionResolver" class="com.ho.exceptionresolver.CustomSimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="com.ho.exceptionresolver.MyBusinessException">businessErrorPage</prop> <prop key="com.ho.exceptionresolver.MyTechnicalException">technicalErrorPage</prop> <prop key="java.lang.Exception">errorPage</prop> </props> </property> </bean>
Note: If a handler is configured for the java.lang.Exception in both Spring and servlet container, then, the Spring configration will have more high priority to handle the exception.
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.Exception">errorPage</prop> </props> </property> </bean>
File: web.xml
<error-page> <exception-type>java.lang.Exception</exception-type> <location>/WEB-INF/jsp/errorPage.jsp</location> </error-page>
5. View Pages
Here, the code of view JSP pages which display the parameters errorTitle and errorMessage added in the CustomSimpleMappingExceptionResolver:
File businessErrorPage.jsp for the MyBusinessException:
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <title>Error page</title> </head> <body> ------- Business Error Page ------- <h1>${errorTitle}</h1> <hr/> <b>${errorMessage}</b> </body> </html>
File technicalErrorPage.jsp for the MyTechnicalException:
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <title>Error page</title> </head> <body> ------- Technical Error Page ------- <h1>${errorTitle}</h1> <hr/> <b>${errorMessage}</b> </body> </html>
File errorPage.jsp for the java.lang.Exception:
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <title>Error page</title> </head> <body> ------- Java Exception ------- <% -- <h1>${exception.customMessage}</h1> --%> <h1>${errorTitle}</h1> <hr/> <b>${errorMessage}</b> </body> </html>
Note: In a JSP page, the exception instance could be accessed via ${exception} EL expression.
6. Other solution
It is possible to handle the exception in presentation layer (Controller) without use of the class “SimpleMappingExceptionResolver” only by create an abstract class MyAbstractController extending the Spring class MultiActionController, and extended by the others Spring controllers in the application. We will override the handleRequestInternal method with a simple try/catch:
package com.ho.exceptionresolver; //... public abstract class MyAbstractController extends MultiActionController{ @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView modelAndView = null; try{ modelAndView = super.doResolveException(request, response, handler, ex); //... }catch(Exception ex){ String errorTitle = ""; if(ex instanceof MyBusinessException){ modelAndView = new ModelAndView("businessErrorPage"); errorTitle = "A specific title for the MyBusinessException exception"; // ... Apply extra business logic: auditing, logging // ...... for the MyBusinessException exception }else if(ex instanceof MyTechnicalException){ modelAndView = new ModelAndView("technicalErrorPage"); errorTitle = "A specific title for the MyTechnicalException exception"; // ... Apply extra business logic: sending emails // ...... for the MyTechnicalException exception }else{ modelAndView = new ModelAndView("errorPage"); errorTitle = "Error standard exception"; } modelAndView.getModelMap().addAttibute("errorTitle", errorTitle ); modelAndView.getModelMap().addAttibute("errorMessage", ex.getMessage()); } return modelAndView; } }
That’s all!!!!
Huseyin OZVEREN
Thank you for sharing your full example 🙂 🙂