SpringMvc(异常处理)
1、内置异常处理解析器
打开 DispatcherServlet.properties
文件,这里面可以看到SpringMvc的一些内置的api,在这个key后面就是我们的异常处理解析器,默认的异常处理类有三个,如下示例:
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
在解析异常的时候,SpringMvc会在会从默认的处理器去搜索相对应的处理器。
1、org.springframework.web.servlet.HandlerExceptionResolver SpringMvc中的默认处理器。
2、org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver 处理SpringMvc中的一般异常。
3、org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver专门处理@ResponseStatus注解类型的异常,@ResponseStatus注解一般会标记在方法上,然后方法抛出一个异常。
4、org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver 处理@ExceptionHandler(例如平时的404,500等异常)。
5、AbstractHandlerMethodExceptionResolver: 是ExceptionHandlerExceptionResolver的父类
6、SimpleMappingExceptionResolver:通过配置的异常类和view的对应关系解析异常,将哪些异常直接指定到哪个视图上面去。
2、统一异常处理
2.1、获得Java处理类的异常信息并输出
注:要导入Tomcat依赖包,才可以使用jsp文件中的命令。
@ExceptionHandler注解如果写在控制器中,只能处理当前控制器的方法。
@Controller
public class ExceptionController {
@RequestMapping("/exception01")
public String exception01(@RequestParam("name") String name){
System.out.println("方法处理中01");
return "show";
}
@RequestMapping("/exception02")
public String exception02(@RequestParam("name") String name){
System.out.println("方法处理中02");
return "show";
}
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("ex",ex);
//日志记录
System.out.println(ex.getMessage());
return modelAndView;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
出错了。<br/>
<%= ((Exception)request.getAttribute("ex")).getMessage() %>
</body>
</html>
2.2、Java异常栈信息输出
别人的代码:
public static String getStackTrace(Throwable throwable){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try {
throwable.printStackTrace(pw);
return sw.toString();
} finally {
pw.close();
}
}
我的代码:
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("ex",ex);
//日志记录,获取到完整的堆栈信息
System.out.println(getStackTrace(ex));
return modelAndView;
}
public static String getStackTrace(Throwable throwable){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try {
throwable.printStackTrace(pw);
return sw.toString();
} finally {
pw.close();
}
}
报错截图:
2.3、处理全局异常
处理异常的优先级
1、全局异常
2、处理器异常
3、全局异常中的具体异常
就近原则,如果自己当前控制器中有异常处理方法,就会用自己的
如果没有,就会用全局的异常处理,如果全局的异常处理有多个
则会精确匹配,匹配到最精准的那个
@ControllerAdvice注解标注在类上时,代表处理全局异常
@ControllerAdvice
public class GeneralExceptionHandler {
/**
* 全局异常处理
* @param ex
* @return
*/
@ExceptionHandler(Exception.class)
public ModelAndView handleException01(Exception ex){
System.out.println("全局异常处理。");
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("ex",ex);
//日志记录,获取到完整的堆栈信息
System.out.println(getStackTrace(ex));
return modelAndView;
}
@ExceptionHandler(MissingServletRequestParameterException.class)
public ModelAndView handleException02(Exception ex){
System.out.println("全局异常处理,参数找不到了。");
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("ex",ex);
//日志记录,获取到完整的堆栈信息
System.out.println(getStackTrace(ex));
return modelAndView;
}
public static String getStackTrace(Throwable throwable){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try {
throwable.printStackTrace(pw);
return sw.toString();
} finally {
pw.close();
}
}
}
2.4、统一处理异常(json或页面)
我们可以通过多种方式去判断什么时候是ajax的json请求进来,什么时候是从页面访问过来,因为我们在做统一异常处理时两种不同场景的处理方式会不同,
集成jackson的pom配置文件如下
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.3</version>
</dependency>
都在代码中写着
/* 1、根据用户请求的方法是否是一个需要返回json数据的方法
// 获得类上面的某个注解
RestController restController = handlerMethod.getClass().getAnnotation(RestController.class);
// 获得方法上的某个注解
ResponseBody responseBody = handlerMethod.getMethod().getAnnotation(ResponseBody.class);
// 如果当前请求是ajax就返回json
if(restController != null || responseBody != null){}*/package cool.ale.exception;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* @author dujlc
*
*
* 统一异常处理:同时返回普通请求和ajax请求
* 普通请求:返回视图,错误信息
* ajax:返回json
* {
* code
* message
* }
*
*/
@ControllerAdvice
public class GeneralExceptionHandler {
/**
* 全局异常处理
* @param ex
* @return
*/
@ExceptionHandler(Exception.class)
public ModelAndView handleException01(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod,
Exception ex) throws IOException {
System.out.println("全局异常处理。");
/* 1、根据用户请求的方法是否是一个需要返回json数据的方法
// 获得类上面的某个注解
RestController restController = handlerMethod.getClass().getAnnotation(RestController.class);
// 获得方法上的某个注解
ResponseBody responseBody = handlerMethod.getMethod().getAnnotation(ResponseBody.class);
// 如果当前请求是ajax就返回json
if(restController != null || responseBody != null){}*/
// 2、如果请求头中的类型包含了 Content-Type 包含 application/json
if (!StringUtils.isEmpty(request.getHeader("Content-Type")) && request.getHeader("Content-Type").indexOf("application/json") > -1){
// 可直接依照下面的方式输出
//response.getWriter().write("sss");
// 但是我们一般是集成jackson,返回json
// ModelAndView既支持json返回、也支持视图返回
// ResponseBody的底层也是做了这个MappingJackson2JsonView
ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
// 通常会根据不同的异常返回不同的编码,这里为了简单,先随便写一种s
modelAndView.addObject("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
modelAndView.addObject("message",ex.getMessage());
return modelAndView;
}
else{
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("ex",ex);
//日志记录,获取到完整的堆栈信息
System.out.println(getStackTrace(ex));
return modelAndView;
}
}
public static String getStackTrace(Throwable throwable){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try {
throwable.printStackTrace(pw);
return sw.toString();
} finally {
pw.close();
}
}
}
2.5、处理404页面
1、在WEB-INF文件夹下创建一个404.html文件
2、spring的配置文件中配置静态资源映射
<mvc:resources mapping="/404.html" location="/"></mvc:resources>
3、web.xml中映射资源
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>