In the post
Spring MVC Exception Handling Tutorial we have already got an overview of how exception handling can be configured in Spring MVC application.
In this post we’ll see a Spring MVC exception handling example using @ExceptionHandler
, @ControllerAdvice
, @ResponseStatus
annotations and by rendering a default error page when exception is not resolved by any HandlerExceptionResolver.
Technologies used
Following is the list of tools used for the Spring MVC exception handling example.
- Spring 5.0.8 Release (Spring core, spring web, spring webmvc).
- Java 10
- Tomcat server V 9.0.10
- Eclipse IDE
- Maven Dependencies
- Project structure
- Spring MVC exception handling example flow
- Spring MVC exception handling – Controller Classes
- Deployment descriptor for Spring MVC exception handling
- Spring Configuration file
- Custom exception class
- Model bean in Spring MVC exception handling example
- Spring MVC exception handling example - Views
- Deploying and running the Spring MVC exception handling example
Maven Dependencies
Maven is used for managing dependencies in this Spring MVC exception handling example.
- Please refer Spring Web MVC Example With Annotations to see how to set Spring MVC project using Maven.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-mvc</groupId> <artifactId>spring-mvc</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>Spring MVC</name> <description>Spring MVC example</description> <properties> <spring.version>5.0.8.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <release>10</release> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.1</version> <configuration> <warSourceDirectory>WebContent</warSourceDirectory> </configuration> </plugin> </plugins> </build> </project>
Project structure
Here is the final project structure of the Spring MVC exception handling example for reference.
Spring MVC exception handling example flow
In this example there is a user registration form (userRegister.jsp
) having fields first name, last name and email. Once the
form is submitted verification of the fields is done and exception is thrown if the values are not as expected.
There is one exception handler method in the controller class itself. There is also a global exception handler class created for
handling exceptions globally using the @ControllerAdvice
annotation. These exception handlers are example of
ExceptionHandlerExceptionResolver
in Spring MVC exception handling.
Bean definition for SimpleMappingExceptionResolver is also configured in the Spring MVC configuration file for handling any
exception of type MyException, which is a custom exception class. This handling covers the SimpleMappingExceptionResolver
in Spring MVC exception handling.
A default error page is also there (defaulterror.jsp
), that error page is displayed if an exception is not resolved
by any HandlerExceptionResolver or if the response status is set to an error status (i.e. 4xx, 5xx).
Spring MVC exception handling – Controller Classes
MessageController.java
import java.io.IOException; import java.sql.SQLException; import javax.servlet.http.HttpServletRequest; import org.netjs.exception.MyException; import org.netjs.model.User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; @Controller public class MessageController { @RequestMapping(value = "/user", method = RequestMethod.GET) public String registerUser(Model model) { model.addAttribute(new User()); return "userRegister"; } @RequestMapping(value = "/registerUser", method = RequestMethod.POST) public String showUser(@ModelAttribute("user") User user, Model model) throws Exception{ model.addAttribute("User", user); if(user.getFirstName().length() < 5) { // is handled by the configured SimpleMappingExceptionResolver throw new MyException("First name too short exception"); }if(user.getEmail().equals("")) { // is handled by the handleIOException method within this controller throw new IOException("Email not entered exception"); }if(!user.getEmail().contains("@")) { // handled in UniversalExceptionController throw new IllegalArgumentException("Email not valid exception"); }if(user.getEmail().equals("123@abc")) { // handled in UniversalExceptionController throw new SQLException("Email not found excpetion"); }if(user.getLastName().length() < 3) { // not resolved by any HandlerExceptionResolver - default error throw new Exception("Last name exception"); }else { return "user"; } } @ExceptionHandler(IOException.class) public ModelAndView handleIOException(HttpServletRequest request, Exception ex){ ModelAndView mv = new ModelAndView(); mv.addObject("exception", ex); mv.setViewName("error"); return mv; } }
Using the handler method registerUser()
(request path - /user), instance of User Model bean is set and then the logical view name is returned which resolves to the registration form.
Submission of the form (request path - /registerUser) is handled by the method showUser()
. In this method there are some field validations that result in different exceptions being thrown.
In the controller class there is an exception handler method handleIOException()
that is used to resolve exceptions
of type IOException. In the exception handler method a new ModelAndView object is created where exception message is set and
the view name is set that resolves to error.jsp
.
UniversalExceptionController.java
Writing exception handlers in each controller will make it a repetitive exercise. It is better to configure a global exception handler which can handle exceptions globally. Following class is set to do that.
import java.sql.SQLException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; @ControllerAdvice public class UniversalExceptionController { @ExceptionHandler({IllegalArgumentException.class, NumberFormatException.class}) public ModelAndView IAExceptionHandler(Exception ex) { ModelAndView mv = new ModelAndView(); mv.addObject("exception", ex); mv.setViewName("error"); return mv; } @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Email not found") @ExceptionHandler(SQLException.class) public void SQLExceptionHandler(Exception ex) { } }
As you can see class is annotated with @ControllerAdvice
annotation. Using this annotation you can set up a class with exception handler methods that are used as global exception handlers.
First method in the class IAExceptionHandler
handles IllegalArgumentException and NumberFormatException thrown
from any controller. As the value of @ExceptionHandler you can provide an array of exception classes if you want to pass more
than one exception class.
Second method uses @ResponseStatus annotation, with HTTP status as not found (404) along with value attribute as SQLException. In case SQLException is thrown from the application it doesn’t resolve the exception just the status code 404 is returned. So it displays the error page configured for request path ("/error"). This path ("/error") is defined in the web.xml through this tag.
<error-page> <location>/error</location> </error-page>
ErrorController.java
This is the controller used for the path ("/error").
import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class ErrorController { @RequestMapping(path = "/error") public ModelAndView handle(HttpServletRequest request) { ModelAndView mv = new ModelAndView(); String errorMsg = ""; int httpErrorCode = getErrorCode(request); // Add more status codes as required if(httpErrorCode == 404) { errorMsg = "Required resource not found, please check the data or contact admin."; }else { errorMsg = "An error occurred in the application, please contact admin."; } mv.addObject("errorMsg", errorMsg); mv.setViewName("defaulterror"); return mv; } private int getErrorCode(HttpServletRequest httpRequest) { return (Integer) httpRequest.getAttribute("javax.servlet.error.status_code"); } }
As you can see the request path is - /error here and this controller returns view as "defaulterror".
Deployment descriptor for Spring MVC exception handling
Apart from the DispatcherServlet and URL mapping configuration web.xml
also has the setup for the error page. This same error page is used for any response status code returned by the application.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <display-name>spring-mvc</display-name> <servlet> <servlet-name>mvcexample</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvcexample</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <error-page> <location>/error</location> </error-page> </web-app>
As you can see the location is /error for the error page, we have already seen the ErrorController that has the handler method for this request path.
Spring Configuration file
The Spring configuration file is as follows.
mvcexample-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <mvc:annotation-driven /> <context:component-scan base-package="org.netjs.controller" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.netjs.exception.MyException">error</prop> </props> </property> <property name="defaultErrorView" value="defaulterror"/> </bean> </beans>
As you can see SimpleMappingExceptionResolver
is configured here to handle exception of type MyException which is a
custom exception class, view name returned is error which resolves to error.jsp. Default error view is
also configured with the value as "defaulterror" which is same as returned by ErrorController. So the same default
error page (defaulterror.jsp) is used if any status code is returned (maps to "/error") and as the default view
(maps to "/defaulterror") if there is no exception handler to handle the thrown exception.
Custom exception class
The custom exception class MyException configured in SimpleMappingExceptionResolver bean definition is as follows.
public class MyException extends RuntimeException { private static final long serialVersionUID = -1292317882781432620L; private String msg; public MyException(String msg){ this.msg = msg; } public MyException(String msg, Throwable cause){ super(cause); this.msg = msg; } @Override public String getMessage() { return msg; } }
Model bean in Spring MVC exception handling example
User.java
public class User { private String firstName; private String lastName; private String email; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
Spring MVC exception handling example - Views
JSPs used in the Spring MVC exception handling example are listed below.
User registration form – userRegister.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <!DOCTYPE html> <html> <head> <meta charset="ISO-8859-1"> <title>User Registration</title> </head> <body> <form:form action="registerUser" modelAttribute="user" method="post"> <table> <tr> <td> <form:label path="firstName">First Name</form:label> </td> <td> <form:input path="firstName" id="firstname" /> </td> </tr> <tr> <td> <form:label path="lastName">Last Name</form:label> </td> <td> <form:input path="lastName" id="lastname" /> </td> </tr> <tr> <td> <form:label path="email">Email</form:label> </td> <td> <form:input path="email" id="email" /> </td> </tr> <tr> <td><input type="submit" value="Submit"></td> </tr> </table> </form:form> </body> </html>
user.jsp
This jsp shows the user data if no exception is thrown.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html> <html> <head> <meta charset="ISO-8859-1"> <title>Spring MVC tutorial - User</title> </head> <body> <table> <tr> <td>First Name: ${User.firstName}</td> </tr> <tr> <td>Last Name: ${User.lastName}</td> </tr> <tr> <td>Email: ${User.email}</td> </tr> </table> </body> </html>
error.jsp
This is the error page which is displayed if an exception is thrown.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html> <html> <head> <meta charset="ISO-8859-1"> <title>Error Page</title> </head> <body> <h3>Error while registering user</h3> Error Message - ${exception.getMessage()} </body> </html>
defaulterror.jsp
This is the default error page which is displayed if the exception is not resolved.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html> <html> <head> <meta charset="ISO-8859-1"> <title>Error Page</title> </head> <body> <!-- if errorMsg is empty that means exception is not handled by any handler and coming here because of the defaultErrorView property in SimpleMappingExceptionResolver. If errorMsg is not empty that means coming through ErrorController (there is a response status code)--> <c:if test="${empty errorMsg}"> <h3>An error occurred in the application, please contact admin.</h3> </c:if> <h3>${errorMsg}</h3> </body> </html>
Deploying and running the Spring MVC exception handling example
Once the Maven build is done and application is deployed in Tomcat server you can run it using this URL-
http://localhost:8080/spring-mvc/user
Refer Spring Web MVC Example With Annotations to see how to do a Maven build and deploy Spring MVC application in Tomcat server.
Here are some of the screen shots for different values.
If length of first name < 5
If email is not entered
If email doesn’t contain @
If email is entered as 123@abc
This leads to SQLException which returns response status code 404 so default error page is displayed.
If last name length < 3
In that case Exception is thrown. Since there is no exception handler method that handles exception of type Exception so it remains unresolved resulting in display of default error page.
That's all for this topic Spring MVC Exception Handling - @ExceptionHandler And @ControllerAdvice Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Spring Tutorial Page
Related Topics
You may also like-
No comments:
Post a Comment