Controller based ExceptionHandler & Global ExceptionHandler
Controller based ExceptionHandler
In this type of Exception handling, we can write handler methods inside the controller class itself.
So to achive this , we need to
a) Define a new method inside the controller
b) Annotate this mehod with @ExceptionHandler and parameter as Exception that we have to handle
c) Handle the Exception inside this method and return the specific view or JSON based on the requirement.
Limitation of this approach is that , only those exceptions which are thrown inside this controller class can be handled.
Any exceptions thrown from outside this controller class will be left unhandled and same exception stack trace will appear in the UI in that case.
We can define as many handler methods as we want with each exception.
But specific exception handled methods will be given highest priority.
So if there are 2 exception handler methods ,method 1 is handling NullPointerException and method 2 is handling Exception , now if Null pointer exception is thrown then method 1 handler will be executed.
We will see below how this can be implemented.
Global ExceptionHandler
In this approach , the implementation is almost similar to @ExceptionHandler approach only.
The only difference here is that , it can handle all the exceptions thrown from any of the controllers in the application.
So it is always advisable to use over second approach.
It can be implemented as
a) Define a new class and anootate this class with @ControllerAdvice
b) Keep on adding new method for each exception using @ExceptionHandler annotation.
So class which is annotated with @ControllerAdvice is considered as global exception handler because methods inside it can handle the exception thrown from any controller in the application.
Lets see the real time example
Define a controller with @ExceptionHandler annotation
- package com.kb.controllers;
- 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 com.kb.exception.InvalidUserException;
- import com.kb.model.User;
- @Controller
- public class LoginController {
- @RequestMapping(value="/displayLoginPage",method=RequestMethod.GET)
- public String displayLoginPage(Model model){
- User user = new User();
- model.addAttribute("user", user);
- return "/login";
- }
- @RequestMapping(value="/doLogin",method=RequestMethod.POST)
- public String doLogin(@ModelAttribute User user,Model model){
- String userName=user.getUserName();
- String password = user.getPassword();
- if("kb".equals(userName) && "1234".equals(password)){
- model.addAttribute("user", user);
- return "/home";
- }
- else{
- //Log the exception
- throw new InvalidUserException("INVALID_USER","User is not Valid for this site");
- }
- }
- @RequestMapping(value="/getGenericException",method=RequestMethod.GET)
- public String getGenericException(){
- throw new Exception();
- }
- @ExceptionHandler(InvalidUserException.class)
- public String handleInvalidUserException(){
- return "userNotFound";
- }
- }
package com.kb.controllers; 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 com.kb.exception.InvalidUserException; import com.kb.model.User; @Controller public class LoginController { @RequestMapping(value="/displayLoginPage",method=RequestMethod.GET) public String displayLoginPage(Model model){ User user = new User(); model.addAttribute("user", user); return "/login"; } @RequestMapping(value="/doLogin",method=RequestMethod.POST) public String doLogin(@ModelAttribute User user,Model model){ String userName=user.getUserName(); String password = user.getPassword(); if("kb".equals(userName) && "1234".equals(password)){ model.addAttribute("user", user); return "/home"; } else{ //Log the exception throw new InvalidUserException("INVALID_USER","User is not Valid for this site"); } } @RequestMapping(value="/getGenericException",method=RequestMethod.GET) public String getGenericException(){ throw new Exception(); } @ExceptionHandler(InvalidUserException.class) public String handleInvalidUserException(){ return "userNotFound"; } }
This controller is a login controller which is having a request mapping for login page.
We have added a method called handleInvalidUserException just to show how the exception can be handled by a method using @ExceptionHandler.
Define a controller with @ControllerAdvice annotation
- package com.kb.controllers;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- @ControllerAdvice
- public class ExceptionHandlerController {
- @ExceptionHandler(Exception.class)
- public String handleGenericException(){
- return "genericError";
- }}
package com.kb.controllers; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class ExceptionHandlerController { @ExceptionHandler(Exception.class) public String handleGenericException(){ return "genericError"; }}
In this controller we have written an handler method for general Exception.
This handler handles all the exceptions thrown from any controller in the application.
So we are throwing an exception from Login controller and its getting handled by this method.
Create the view page for exception handling
userNotFound.jsp
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Insert title here</title>
- </head>
- <body>
- <h2> Sorry, some technical error, Please try again</h2>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h2> Sorry, some technical error, Please try again</h2> </body> </html>
genericError.jsp
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Insert title here</title>
- </head>
- <body>
- <h2> Sorry, some technical error, Please try again</h2>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h2> Sorry, some technical error, Please try again</h2> </body> </html>
Create the view page for login and home page
login.jsp
- <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
- <html>
- <head>
- <title>Spring MVC Exception Handling</title>
- </head>
- <body>
- <h2>Enter below details to Login</h2>
- <form:form method="POST" modelAttribute="user" action="doLogin">
- <table>
- <tr>
- <td>Enter your User Name</td>
- <td><form:input path="userName" /></td>
- </tr>
- <tr>
- <td>Enter your password:</td>
- <td><form:password path="password" showPassword="true"/></td>
- </tr>
- <tr>
- <td><input type="submit" name="submit" value="Click here to Login"></td>
- </tr>
- </table>
- </form:form>
- </body>
- </html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <html> <head> <title>Spring MVC Exception Handling</title> </head> <body> <h2>Enter below details to Login</h2> <form:form method="POST" modelAttribute="user" action="doLogin"> <table> <tr> <td>Enter your User Name</td> <td><form:input path="userName" /></td> </tr> <tr> <td>Enter your password:</td> <td><form:password path="password" showPassword="true"/></td> </tr> <tr> <td><input type="submit" name="submit" value="Click here to Login"></td> </tr> </table> </form:form> </body> </html>
home.jsp
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Login Success</title>
- </head>
- <body>
- <div align="center">
- <h2>Welcome ${user.userName}! You have been successfully Logged in.</h2>
- </div>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Login Success</title> </head> <body> <div align="center"> <h2>Welcome ${user.userName}! You have been successfully Logged in.</h2> </div> </body> </html>
Create the model class
- package com.kb.model;
- public class User {
- private String userName;
- private String password;
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- }
package com.kb.model; public class User { private String userName; private String password; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Create spring configuration file
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.2.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
- <context:component-scan base-package="com.kb.*" />
- <mvc:annotation-driven />
- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/WEB-INF/pages/" />
- <property name="suffix" value=".jsp" />
- </bean>
- </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <context:component-scan base-package="com.kb.*" /> <mvc:annotation-driven /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
Create the web.xml
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
- version="3.1">
- <display-name>Controller Advice Example</display-name>
- <!-- Spring MVC dispatcher servlet -->
- <servlet>
- <servlet-name>mvc-dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-mvc.xml,
- </param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>mvc-dispatcher</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Loads Spring Security configuration file -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-mvc.xml,
- </param-value>
- </context-param>
- </web-app>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Controller Advice Example</display-name> <!-- Spring MVC dispatcher servlet --> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-mvc.xml, </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Loads Spring Security configuration file --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-mvc.xml, </param-value> </context-param> </web-app>
Create the pom.xml
- <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/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>Spring</groupId>
- <artifactId>ControllerAdviceExample</artifactId>
- <packaging>war</packaging>
- <version>0.0.1-SNAPSHOT</version>
- <name>ControllerAdviceExample Maven Webapp</name>
- <url>http://maven.apache.org</url>
- <properties>
- <org.springframework.version>4.2.0.RELEASE</org.springframework.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <version>2.5</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>javax.servlet.jsp.jstl</groupId>
- <artifactId>javax.servlet.jsp.jstl-api</artifactId>
- <version>1.2.1</version>
- </dependency>
- <dependency>
- <groupId>taglibs</groupId>
- <artifactId>standard</artifactId>
- <version>1.1.2</version>
- </dependency>
- </dependencies>
- <build>
- <finalName>ControllerAdviceExample</finalName>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.5.1</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </project>
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Spring</groupId> <artifactId>ControllerAdviceExample</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>ControllerAdviceExample Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <org.springframework.version>4.2.0.RELEASE</org.springframework.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>javax.servlet.jsp.jstl-api</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies> <build> <finalName>ControllerAdviceExample</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
Lets build and deploy the application.
Access below url
http://localhost:8080/ControllerAdviceExample/displayLoginPage
Now enter invalid credentials as below
See the below output which comes from the userNotFound.jsp
This exception is thrown in the login controller and handled by the handleInvalidUserException method and returns the userNotFound view as above.
Now access the below url
http://localhost:8080/ControllerAdviceExample/getGenericException
This url calls the method getGenericException in the login controller and we are intentionally throwing the exception in this method.
This exception is not handled in the login controller but it is handled inside a separate controller called ExceptionHandlerController which returns the genericError jsp page.
Like this, exception thrown from any controller can be handled from a @Controlleradvice controller.