Spring MVC Exception Handling and Global Exception Example

Spring provides a robust exception handling framework. We can handle exceptions using @ResponseStatus, @ExceptionHandler, HandlerExceptionResolver and @ControllerAdvice. @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.

spring-mvc-exception-handling-with-exceptionhandler-responseStatus-handlerexceptionresolver-example-global-exception-0.jpg

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");
	}
}

When we throw this exception from anywhere in our application, 404 status code will be obtained and with a message defined in reason element. In our controller class, simply throw this exception. 

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";
	}
}

The mechanism will be that KeywordNotFoundException will return 404 status code and if our global exception handling is catching this code, then accordingly it will be handled. Otherwise 404 error will be thrown with the message given in reason element of @ResponseStatus in KeywordNotFoundException class. After deployment of code, if we access the URL 

http://localhost:8080/concretepage-1/keyword/info?key=key1011 

We will get the output.

spring-mvc-exception-handling-with-exceptionhandler-responseStatus-handlerexceptionresolver-example-global-exception-1.jpg

Here we have handled 404 globally.

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";
	  }
}

The mechanism is that when we throw the exception, spring searches exception handler for that exception type and then it is handled accordingly. If we do not provide return type i.e void then this method must have @ResponseStatus defined. Any global exception handling can catch it if defined for that status code. If no exception handler is present in that controller class, then again it will be caught by global exception if defined. 

1. If we run the URL http://localhost:8080/concretepage-1/myworld/check?id=1 The output will be

spring-mvc-exception-handling-with-exceptionhandler-responseStatus-handlerexceptionresolver-example-global-exception-2.jpg

This output is because for the SQLException type, the handler has void return type and it throws 409 status code. There is no global exception defined to catch 409 status code. So the reason defined with @ResponseStatus is displayed with exception. 

2. Now run the URL http://localhost:8080/concretepage-1/myworld/check?id=2 The output will be as print screen.

spring-mvc-exception-handling-with-exceptionhandler-responseStatus-handlerexceptionresolver-example-global-exception-3.jpg

For the FileNotFoundException exception type our handler is returning error page. We have defined it in our handler method. 

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>

To iterate the stack trace , we can use JSTL. 

3. Run the URL http://localhost:8080/concretepage-1/myworld/check?id=3 and we will get output.

spring-mvc-exception-handling-with-exceptionhandler-responseStatus-handlerexceptionresolver-example-global-exception-4.jpg

For the IOException, there is no handler at controller level, so it will be handled by global exception handler method.

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";
	  }
}

Whenever an exception is thrown and is not handled at controller level, then it will be caught by global exception handler method. @ControllerAdvice makes available the global exception class for every controller in our application. In this way, spring also handles the error specific to status code. For the example, we have created two handler methods in our global exception class. Find the view for them. 

globalerror.jsp

<html>
<head>
<title> Global Error </title>
</head>
	<body>
	<h1>Error:  ${exception.message}</h1>
	</body>
</html>

Find the page for 404 defined in global exception class. 

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;
   }
}

For the example, we have created a custom exception class and it has mapped with SimpleMappingExceptionResolver bean. 

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");
	}
}

For the demo, we have a mapped view which will be displayed for the error defined with SimpleMappingExceptionResolver. 

error.jsp

<html>
<head>
<title> Spring MVC Exception </title>
</head>
	<body>
	<h1>Error:  ${exc.message}</h1>
	</body>
</html>

Find the controller which we have created for the demo of exception handling using SimpleMappingExceptionResolver. 

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";
	}
}

To check the output, run the URL http://localhost:8080/concretepage-1/myvillage/info?vid=111 we will get result as given print screen.

spring-mvc-exception-handling-with-exceptionhandler-responseStatus-handlerexceptionresolver-example-global-exception-5.jpg

Find the WebApplicationInitializer implementation being used in our demo. 

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);  
   }  
}

For the success, the view is as given below. 

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!

参考资料:

本文标题:Spring MVC Exception Handling and Global Exception Example

本文链接:http://yedward.net/?id=407

本文版权归作者所有,欢迎转载,转载请以文字链接的形式注明文章出处。

相关文章