Spring MVC Exception Handling
June 28, 2019
Spring provides a robust exception handling framework. We can handle exceptions using @ResponseStatus, @ExceptionHandler, HandlerExceptionResolver and @ControllerAdvice in Spring MVC. @ResponseStatus defines a status code for the given exception and can be used at custom exception class level and method level with @ExceptionHandler in controller. In controller class, we can define handler method using @ExceptionHandler and this will be controller specific. To handle exception globally, spring provides @ControllerAdvice that will be available for every controller. Define a class for global exception and annotate it with @ControllerAdvice and we need to define methods using @ExceptionHandler annotation within the class. Spring provides one more approach to handle exception. Use HandlerExceptionResolver in spring XML or java configuration where we can define mappings of exception type and view name. Find the complete example with description.
@ExceptionHandler
@ExceptionHandler annotation handles exceptions in spring MVC. We annotate our controller methods by this annotation. That method can have arguments of type Exception, HttpServletRequest, HttpServletResponse, Session, WebRequest etc in any order. That can return ModelAndView, Model, Map, View, String, @ResponseBody and void. In case of return type void, we can redirect the response with the object of HttpServletResponse .@ResponseStatus
@ResponseStatus can be applied on custom exception class or any controller method. It has two elements value and reason. Using value element, we assign the response status code like 404, 200 etc. Reason element is used for response. We can write a statement as a reason.HandlerExceptionResolver
HandlerExceptionResolver is an interface that has different implementations to resolve exception thrown during execution. Some implementations are ExceptionHandlerExceptionResolver, HandlerExceptionResolverComposite, SimpleMappingExceptionResolver etc. In our example we will use SimpleMappingExceptionResolver. It maps exception type with a view name. So it represents error view and can be used with any error type.@ControllerAdvice
@ControllerAdvice annotation is auto detected by classpath scanning. In java configuration, we must use @EnableWebMvc. We can use it for @ExceptionHandler to provide global exception handling in spring. What we need to do is that annotate the class with @ControllerAdvice and methods of this class should be annotated with @ExceptionHandler. In our example, we will use @ControllerAdvice for the global exception handling demo.Software required
To run the demo, we need required software and tools.1. Java 7
2. Tomcat 8
3. Eclipse
4. Gradle
5. Spring 4
Project Structure in Eclipse
Find the project structure screen shot in eclipse.Gradle File to Resolve JAR Dependencies
Find the Gradle to resolve JAR dependencies.build.gradle
apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'war' archivesBaseName = 'concretepage' version = '1' repositories { mavenCentral() } dependencies { compile 'org.springframework.boot:spring-boot-starter-web:1.2.2.RELEASE' compile 'jstl:jstl:1.2' providedCompile 'org.springframework.boot:spring-boot-starter-tomcat:1.2.2.RELEASE' }
Exception Handling using @ResponseStatus
To handle exception using @ResponseStatus, we have to annotate our custom exception class with it. Declare a reason and status code.KeywordNotFoundException.java
package com.concretepage.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Keyword") public class KeywordNotFoundException extends RuntimeException { private static final long serialVersionUID = 1L; public KeywordNotFoundException(String key){ super(key+" not available"); } }
KeywordController.java
package com.concretepage.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import com.concretepage.exception.KeywordNotFoundException; @Controller @RequestMapping("/keyword") public class KeywordController { @RequestMapping("/info") public String info(@RequestParam(value="key") String key, Model model) { if ("key101".equals(key)) { model.addAttribute("msg", "Hello Key World!"); } else { throw new KeywordNotFoundException(key); } return "success"; } }
http://localhost:8080/concretepage-1/keyword/info?key=key1011
We will get the output.
Exception Handling using @ExceptionHandler
@ExceptionHandler is used at method level in classes annotated by @Controller and @ControllerAdvice. To handle exception at controller level , define method for each exception annotated with @ExceptionHandler, which we need to use and if needed we can use @ResponseStatus with @ExceptionHandler. While defining exception handler method, we also define view name, exception object name etc.MyWorldExceptionController.java
package com.concretepage.controller; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/myworld") public class MyWorldExceptionController { @ResponseStatus(value=HttpStatus.CONFLICT, reason="Data already present") @ExceptionHandler(SQLException.class) public void dataConflict() { System.out.println("----Caught SQLException----"); } @ExceptionHandler(FileNotFoundException.class) public ModelAndView myError(Exception exception) { System.out.println("----Caught FileNotFoundException----"); ModelAndView mav = new ModelAndView(); mav.addObject("exc", exception); mav.setViewName("myerror"); return mav; } @RequestMapping("/check") public String myInfo(@RequestParam(value="id") String id, Model model) throws Exception { if ("1".equals(id)) { throw new SQLException(); }else if ("2".equals(id)) { throw new FileNotFoundException("File not found."); }else if ("3".equals(id)) { throw new IOException("Found IO Exception"); }else { model.addAttribute("msg", "Welcome to My World."); } return "success"; } }
1. If we run the URL http://localhost:8080/concretepage-1/myworld/check?id=1 The output will be
2. Now run the URL http://localhost:8080/concretepage-1/myworld/check?id=2 The output will be as print screen.
myerror.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <title> Spring MVC Exception </title> </head> <body> <h1>Error : ${exc.message}</h1> <c:forEach items="${exc.stackTrace}" var="st"> ${st} </c:forEach> </body> </html>
3. Run the URL http://localhost:8080/concretepage-1/myworld/check?id=3 and we will get output.
Global Exception Handling using @ControllerAdvice
To handle global exception in spring, it provides @ControllerAdvice annotation. Create a class using it and define methods using @ExceptionHandler.GlobalExceptionHandler.java
package com.concretepage.controller; import java.io.IOException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; import com.concretepage.exception.KeywordNotFoundException; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(IOException.class) public ModelAndView myError(Exception exception) { System.out.println("----Caught IOException----"); ModelAndView mav = new ModelAndView(); mav.addObject("exception", exception); mav.setViewName("globalerror"); return mav; } @ExceptionHandler(KeywordNotFoundException.class) public String notFound() { System.out.println("----Caught KeywordNotFoundException----"); return "404"; } }
globalerror.jsp
<html> <head> <title> Global Error </title> </head> <body> <h1>Error: ${exception.message}</h1> </body> </html>
404.jsp
<html> <head> <title> Spring MVC Exception </title> </head> <body> <h1>404 Exception</h1> </body> </html>
Exception Handling using SimpleMappingExceptionResolver
We have one more option to define exception and view mapping i.e HandlerExceptionResolver. SimpleMappingExceptionResolver is the implementation class of HandlerExceptionResolver. We are defining SimpleMappingExceptionResolver bean in java configuration. Instantiate and assign mapping of exception and view name. We can also define the default error view and exception object with it.AppConfig.java
package com.concretepage.config; import java.util.Properties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import org.springframework.web.servlet.view.JstlView; import org.springframework.web.servlet.view.UrlBasedViewResolver; @Configuration @ComponentScan("com.concretepage.controller") @EnableWebMvc public class AppConfig extends WebMvcConfigurerAdapter { @Bean public UrlBasedViewResolver urlBasedViewResolver() { UrlBasedViewResolver resolver = new UrlBasedViewResolver(); resolver.setPrefix("/views/"); resolver.setSuffix(".jsp"); resolver.setViewClass(JstlView.class); return resolver; } @Bean public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties errorMaps = new Properties(); errorMaps.setProperty("ElectricityNotFoundException", "error"); errorMaps.setProperty("NullPointerException", "error"); resolver.setExceptionMappings(errorMaps); resolver.setDefaultErrorView("globalerror"); resolver.setExceptionAttribute("exc"); return resolver; } }
ElectricityNotFoundException.java
package com.concretepage.exception; public class ElectricityNotFoundException extends RuntimeException { private static final long serialVersionUID = 1L; public ElectricityNotFoundException(String villageName) { super(villageName+":Electricity not available"); } }
error.jsp
<html> <head> <title> Spring MVC Exception </title> </head> <body> <h1>Error: ${exc.message}</h1> </body> </html>
VillageController.java
package com.concretepage.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import com.concretepage.exception.ElectricityNotFoundException; @Controller @RequestMapping("/myvillage") public class VillageController { @RequestMapping("/info") public String myInfo(@RequestParam(value="vid") String vid, Model model) throws Exception { if ("111".equals(vid)) { throw new ElectricityNotFoundException("Dhananajaypur"); }else if ("222".equals(vid)) { throw new NullPointerException("Data not found."); }else { model.addAttribute("msg", "Welcome to My Village."); } return "success"; } }
WebAppInitializer.java
package com.concretepage.config; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration.Dynamic; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public class WebAppInitializer implements WebApplicationInitializer { public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(AppConfig.class); ctx.setServletContext(servletContext); Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx)); dynamic.addMapping("/"); dynamic.setLoadOnStartup(1); } }
success.jsp
<html> <head> <title> Spring MVC Success </title> </head> <body> <h1>Message : ${msg}</h1> </body> </html>
Now we are done with spring exception handling. Happy Spring Learning!