spring异常处理机制

点击阅读原文


Spring提供了一系列健全的异常处理框架。我们可以采用@ResponseStatus@ExceptionHandlerHandlerExceptionResolver以及 @ControllerAdvice处理异常。@ResponseStatus可以为异常指定状态码,可以应用到用户定义的异常类以及controller中被@ExceptionHandler注解的方法上。在controller中,我们可以通过使用@ExceptionHandler注解异常处理方法,被注解的方法只能在当前controller内使用。Spring还提供了@ControllerAdvice用于全局异常处理,对所有的controller都有效。定义一个全局异常处理类,并且用@ControllerAdvice注解,在类中需要定义异常处理方法并且用@ExceptionHandler注解。Spring提供了很多种方法处理异常,在XML文件或者java文件中可以使用HandlerExceptionResolver定义异常类型与view名称的映射。下面是一些完整的例子。

 

@ExceptionHandler

SpringMVC@ExceptionHandler注解用来处理异常,我们使用这个注解来标注controller方法。如此,被注解的方法可以有ExceptionHttpServletRequesHttpServletResponseSessionWebRequest等任何顺序的参数类型,方法返回值可以是ModelAndView, Model,Map, View, String, @ResponseBody甚至void类型。返回void类型时, 我们也可以利用HttpServletResponse重新定向。

 

@ResponseStatus

@ResponseStatus可以用在定义的类或者controller方法上,它包含两个元素,valuereasonValue用于设置response的状态码,例如404,200等,reason用于响应,可以是内容语句。

 

HandlerExceptionResolver

HandlerExceptionResolver是各个执行时异常处理的接口,它有各种各样的实现,例如ExceptionHandlerExceptionResolver HandlerExceptionResolverComposite,SimpleMappingExceptionResolver等。在例子中,我们采用了SimpleMappingExceptionResolver。它将异常与一个view名称映射,因此它可以展现error页面适用于任何error类型。

 

@ControllerAdvice

@ControllerAdvice注解是在classpath扫描时自动检测的,java配置时我们须要使用@EnableWebMvcSpring中使用@ControllerAdvice@ExceptionHandler做全局异常处理,使用@ControllerAdvice注解定义的类,@ExceptionHandler注解定义类中的方法。底下的例子中,我们使用@ControllerAdvice做全局异常处理。

运行底下的demo,需要的软件环境:

1.Java 7

2.Tomcat 8

3.Eclipse

4.Gradle

5.Spring 4

 

Eclipse中的工程结构图:

spring异常处理机制_第1张图片


Jar依赖文件的Gradle配置: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'
}

 

例子开始啦,注意注意注意!

 

为了能够采用@ResponseStatus处理异常,我们必须使用ResponseStatus注解我们定义的异常,并且声明状态码和reason

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

 

无论我们在应用的任何地方抛出这个异常,404状态码和reason定义的message将会被接收到,在controller中,抛出这个异常,如下:

 

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

 

KeywordNotFoundException异常抛出后会返回404状态码,全局的异常处理机制会抓取到这个状态码并且处理。否则,404状态码会和@ResponseStatus中定义的消息一起抛出。部署成功后,访问http://localhost:8080/concretepage-1/keyword/info?key=key1011我们会得到如下界面:


spring异常处理机制_第2张图片


@ExceptionHandler异常处理:

@ExceptionHandler应用在被@controller@ControllerAdvice注解的controller中的方法上。为了在controller内部处理异常,我们使用这个注解标注方法,如果需要的话也可以同时使用@ExceptionHandle@ResponseStatus。定义异常处理方法时,同时也定义view名称,异常对象名称等。


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

 

当抛出异常时,Spring针对每一个异常类型搜索对应的异常处理方法来处理异常。如果我们返回值为void时,必须使用@ResponseStatus注解方法。任何的全局异常处理方法会抓取到这个状态码。异常处理的机制是,当前controller内没有异常处理方法的话,该异常会被全局异常处理机制抓取到。

 

访问http://localhost:8080/concretepage-1/myworld/check?id=1

spring异常处理机制_第3张图片


由于SQLEXCEPTION的类型,处理方法返回值类型为void且返回状态码为409。全局异常并没有定义409状态码,因此@ResponseStatus定义的reason和状态码一起显示。


访问http://localhost:8080/concretepage-1/myworld/check?id=2

文件找不到异常,我们返回了错误页,该错误页在处理方法中定义了。

spring异常处理机制_第4张图片


myerror.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>


 Spring MVC Exception 

	
	

Error : ${exc.message}

${st}

 

访问http://localhost:8080/concretepage-1/myworld/check?id=3

spring异常处理机制_第5张图片


Controller内部没有定义方法捕获IO异常,因此它被全局处理机制抓获。

使用@ControllerAdvice做全局异常处理

为了处理全局异常,spring提供了@ControllerAdvice来注解定义的handler类,类中处理方法用@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";
	  }
} 

 

无论什么时候一个异常被抛出,若它没有被controller内部抓获,它将会被全局异常处理机制抓获。@ControllerAdvice使得全局异常处理对于应用内任何一个controller抛出的异常都可以抓获。这样,spring可以处理具体的error为具体的状态码。举个栗子,我们在全局异常handler类中创建了两个异常处理方法,如下:


globalerror.jsp



 Global Error 

	
	

Error: ${exception.message}


404.jsp



 Spring MVC Exception 

	
	

404 Exception

 

使用SimpleMappingExceptionResolver处理异常

我们有很多方式定义异常和view的映射,例如HandlerExceptionResolverSimpleMappingExceptionResolverHandlerExceptionResolver的实现类,我们在java配置中定义SimpleMappingExceptionResolverbean,实例化异常和映射view。我们也可以用它定义默认error页面和异常对象。


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


举个栗子,我们创建了用户异常,并且与SimpleMappingExceptionResolver  bean映射。Demo里面,我们将定义了的error页面与SimpleMappingExceptionResolver 映射。

 

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



 Spring MVC Exception 

	
	

Error: ${exc.message}


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

 

Demo里,我们用SimpleMappingExceptionResolver处理了Controller异常。

 

访问http://localhost:8080/concretepage-1/myvillage/info?vid=111

spring异常处理机制_第6张图片

 

Demo里面的WebAppInitializer实现。

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



 Spring MVC Success 

	
	

Message : ${msg}

Spring异常处理到此结束,真是一个愉快的旅程!

PS: 想要下载源码的,请阅读原文下载代码。


你可能感兴趣的:(spring异常处理机制)