文章目录
- 一、前言
- 二、Spring全局异常编写(3.4 全局异常第三种,实现HandlerExceptionResolver接口)
- 2.1 定义异常处理类,实现HandlerExceptionResolver接口
- 2.2 使用注解或xml配置的方式,将这个异常处理类注入到spring ioc容器
- 2.3 实际业务逻辑中
- 三、SpringMVC全局异常编写
- 3.1 定义服务器错误WEB.XML整合Spring MVC
- 3.1.1 web.xml
- 3.1.2 applicationContext.xml
- 3.2 Spring全局异常第一种,代码侵入方式/Controller增强方式( Advising Controllers)【@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常及其子异常 】
- 3.2.1 异常抛出:业务逻辑中throw抛出
- 3.2.2 第一种,异常捕获/异常处理类返回String,表示消息:@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常
- 3.2.3 第二种,全局异常处理类返回ModelAndView,表示页面:@ControllerAdvice标注后就是异常处理类,@ExceptionHandler(value = Xxx.class) 表示处理的异常
- 3.3 Spring全局异常第二种,xml配置文件方式【配置SimpleMappingExceptionResolver到springioc容器中】
- 3.3.1 异常抛出:业务逻辑中throw抛出
- 3.3.2 异常捕获:applicationContext.xml,配置SimpleMappingExceptionResolver到springioc容器中
- 3.3.3 对应500错误的view jsp页面
- 3.4 Sping全局异常第三种,实现异常捕获接口HandlerExceptionResolver
- 3.4.1 异常抛出:业务逻辑中throw抛出
- 3.4.2 实现异常捕获接口HandlerExceptionResolver,重写resolveException()方法
- 3.4.3 异常捕获处理类注入到Spring IOC容器
- 3.5 使用Restful的Controller可以使用@ResponseBody处理错误
- 3.5.1 自定义类ErrorInfo
- 3.5.2 一个@ResponseBody返回一个错误实例
- 四、springboot如何添加全局异常捕获类(全局异常处理第一种,controller增强)
- 4.1 业务逻辑代码中抛出异常
- 4.2 定义全局异常处理类
- 4.3 运行结果
- 五、面试金手指(面试如何回答,面试语言组织)
- 5.1 起手式,全局异常捕获的两种方式
- 5.2 五种方式
- 六、小结
一、前言
为什么要额外定义一个全局异常处理类?
回答:logger.error()等打印到处都有,开发的时候很不方便,所以将异常统一处理。
二、Spring全局异常编写(3.4 全局异常第三种,实现HandlerExceptionResolver接口)
2.1 定义异常处理类,实现HandlerExceptionResolver接口
定义异常处理类,实现HandlerExceptionResolver接口
package org.xxx.ac.zpk.exception;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.wpwl.ac.utils.JSONUtils;
import org.wpwl.ac.zpk.entity.ApiResult;
/**
* 异常处理
* @author xxx
*
*/
public class ExceptionHandler implements HandlerExceptionResolver {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView();
/* 使用response返回 */
response.setStatus(HttpStatus.OK.value()); // 设置状态码
response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 设置ContentType
response.setCharacterEncoding("UTF-8"); // 避免乱码
response.setHeader("Cache-Control", "no-cache, must-revalidate");
try {
ApiResult json = getExceptionResult(ex); // 调用自定义方法,得到异常处理结果
response.getWriter().write(JSONUtils.bean2Json(json));
} catch (IOException e) {
logger.error("与客户端通讯异常:" + e.getMessage(), e);
}
logger.debug("异常:" + ex.getMessage(), ex);
return mv;
}
private ApiResult getExceptionResult(Exception ex) {
ApiResult result;
// 根据不同的异常类型进行不同处理
if (ex instanceof WPException) {
result = ApiResult.err(ex.getMessage());
} else {
result = ApiResult.err("服务异常!");
}
return result;
}
}
2.2 使用注解或xml配置的方式,将这个异常处理类注入到spring ioc容器
<bean id="exceptionResolver" class="org.wpwl.ac.zpk.exception.ExceptionHandler"/>
2.3 实际业务逻辑中
package org.wpwl.ac.zpk.exception;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.wpwl.ac.zpk.entity.ApiResult;
public class ExceptionController {
// private static final Logger logger = LoggerFactory.getLogger(BaseController.class); // 注释掉异常打印
@ExceptionHandler
@ResponseBody
public ApiResult exception(HttpServletRequest request, Exception e) {
//添加自己的异常处理逻辑,如日志记录
request.setAttribute("exceptionMessage", e.getMessage());
// e.printStackTrace(); // 标准代码中不使用这个打印方式
// logger.error(e.getMessage()); // 这样打印会导致到处都有,太杂乱了
// 根据不同的异常类型进行不同处理 就是上面的getExceptionResult()方法
if(e instanceof WPException){
return ApiResult.err(e.getMessage());
}
else{
return ApiResult.err("服务异常!");
}
}
}
三、SpringMVC全局异常编写
Spring MVC开发WEB应用时捕获全局异常的方法基本有两种:
- WEB.XML,就是指定error-code和page到指定地址,这也是最传统和常见的做法
- 用Spring的全局异常捕获功能,这种相对可操作性更强一些,可根据自己的需要做一后善后处理,比如日志记录等。
SO,列出Spring-MVC做WEB开发时常用全局异常捕获的几种解决方案抛砖引玉,互相没有依赖,每个都可单独使用!
3.1 定义服务器错误WEB.XML整合Spring MVC
3.1.1 web.xml
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>
<!-- 未捕获的错误,同样可指定其它异常类,或自定义异常类 -->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/uncaughtException</location>
</error-page>
3.1.2 applicationContext.xml
<!-- 错误路径和错误页面,注意指定viewResolver -->
<mvc:view-controller path="/404" view-name="404"/>
<mvc:view-controller path="/500" view-name="500"/>
<mvc:view-controller path="/uncaughtException" view-name="uncaughtException"/>
小结:第一种方式,定义服务器错误WEB.XML整合Spring MVC,好了。
3.2 Spring全局异常第一种,代码侵入方式/Controller增强方式( Advising Controllers)【@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常及其子异常 】
3.2.1 异常抛出:业务逻辑中throw抛出
@Controller
public class MainController {
@ResponseBody
@RequestMapping("/")
public String main(){
throw new NullPointerException("NullPointerException Test!");
}
}
3.2.2 第一种,异常捕获/异常处理类返回String,表示消息:@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常
//注意使用注解@ControllerAdvice作用域是全局Controller范围,即必须与抛出异常的method在同一个controller
//可应用到所有@RequestMapping类或方法上的@ExceptionHandler、@InitBinder、@ModelAttribute,在这里是@ExceptionHandler
@ControllerAdvice
public class AControllerAdvice {
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleIOException(NullPointerException ex) {
return ClassUtils.getShortName(ex.getClass()) + ex.getMessage();
}
}
3.2.3 第二种,全局异常处理类返回ModelAndView,表示页面:@ControllerAdvice标注后就是异常处理类,@ExceptionHandler(value = Xxx.class) 表示处理的异常
为了确保@ResponseStatus标注的异常(就是3.2.2 )被Spring框架处理,可以这样编写全局异常处理类:
@ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW); // 就是跳转到名为error.jsp的页面
return mav;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@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=UTF-8">
<title>500 Error</title>
</head>
<body>
<% Exception ex = (Exception)request.getAttribute("exception"); %> // 得到ex
<H2>Exception: <%= ex.getMessage()%></H2> // 使用ex
<P/> // 空格
<% ex.printStackTrace(new java.io.PrintWriter(out)); %> // 使用ex
</body>
</html>
第二种方式,Spring全局异常,代码侵入方式/Controller增强方式( Advising Controllers),完成。
3.3 Spring全局异常第二种,xml配置文件方式【配置SimpleMappingExceptionResolver到springioc容器中】
3.3.1 异常抛出:业务逻辑中throw抛出
@Controller
public class MainController {
@ResponseBody
@RequestMapping("/")
public String main(){
throw new NullPointerException("NullPointerException Test!");
}
}
3.3.2 异常捕获:applicationContext.xml,配置SimpleMappingExceptionResolver到springioc容器中
applicationContext.xml配置文件:
<!-- 全局异常配置 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">errors/500</prop>
<prop key="java.lang.Throwable">errors/500</prop>
</props>
</property>
<property name="statusCodes">
<props>
<prop key="errors/500">500</prop>
</props>
</property>
<!-- 设置日志输出级别,不定义则默认不输出警告等错误日志信息 -->
<property name="warnLogCategory" value="WARN"></property>
<!-- 默认错误页面,当找不到上面mappings中指定的异常对应视图时,使用本默认配置 -->
<property name="defaultErrorView" value="errors/500"></property>
<!-- 默认HTTP状态码 -->
<property name="defaultStatusCode" value="500"></property>
</bean>
3.3.3 对应500错误的view jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@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=UTF-8">
<title>500 Error</title>
</head>
<body>
<% Exception ex = (Exception)request.getAttribute("exception"); %> // 得到ex
<H2>Exception: <%= ex.getMessage()%></H2> // 使用ex
<P/> // 空格
<% ex.printStackTrace(new java.io.PrintWriter(out)); %> // 使用ex
</body>
</html>
3.4 Sping全局异常第三种,实现异常捕获接口HandlerExceptionResolver
3.4.1 异常抛出:业务逻辑中throw抛出
ps:这里全局异常处理和 系统异常/自定义异常 无关
异常抛出:抛出自定义异常
@ResponseBody
@RequestMapping("/ce")
public String ce(CustomException e){
throw new CustomException("msg",e); // 可以throw new 系统异常();
}
这里抛出自定义异常
public class CustomException extends RuntimeException {
public CustomException(){
super();
}
public CustomException(String msg, Throwable cause){
super(msg, cause);
//Do something...
}
}
3.4.2 实现异常捕获接口HandlerExceptionResolver,重写resolveException()方法
public class CustomHandlerExceptionResolver implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("e", e);
//这里可根据不同异常引起类做不同处理方式,本例做不同返回页面。
String viewName = ClassUtils.getShortName(e.getClass()); // viewName
return new ModelAndView(viewName, model);
}
}
新的的HandlerExceptionResolver实现类只需在配置文件中定义即可,可以配置优先级。DispatcherServlet初始化HandlerExceptionResolver的时候会自动寻找容器中实现了HandlerExceptionResolver接口的类,然后添加进来。
3.4.3 异常捕获处理类注入到Spring IOC容器
<bean class="cn.bg.controller.CustomHandlerExceptionResolver"/>
3.5 使用Restful的Controller可以使用@ResponseBody处理错误
使用Restful的Controller可以使用@ResponseBody处理错误,首先定义一个错误:
3.5.1 自定义类ErrorInfo
public class ErrorInfo {
public final String url;
public final String ex;
public ErrorInfo(String url, Exception ex) {
this.url = url;
this.ex = ex.getLocalizedMessage();
}
}
3.5.2 一个@ResponseBody返回一个错误实例
通过一个@ResponseBody返回一个错误实例:
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MyBadDataException.class)
@ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) {
return new ErrorInfo(req.getRequestURL(), ex);
}
使用Restful的Controller可以使用@ResponseBody处理错误,好了。
四、springboot如何添加全局异常捕获类(全局异常处理第一种,controller增强)
作为后端开发专注开发接口。对于程序中出现的异常如果不进行处理,将报错信息直接返回到前端是不优雅的,因此需要对异常进行捕获和处理,但是每个接口都单独处理异常则显得代码十分臃肿,因此写了个异常捕获类对全局出现的异常进行统一处理。
4.1 业务逻辑代码中抛出异常
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(String idNum) {
throw new BusinessException("抛出业务异常");
}
}
4.2 定义全局异常处理类
全局异常处理类要处理整个系统中的所有异常,当然包括刚才那个 HelloController 中的 BusinessException :
金手指:全局异常处理包括两个维度 整个系统 和 各种类型异常
//@ControllerAdvice 注解@ControllerAdvice作用域是全局Controller范围,即必须与抛出异常的method在同一个controller
@ControllerAdvice
public class GlobalExceptionHandler {
//@ExceptionHandler 该注解声明异常处理方法
//value=BusinessException.classb表示处理抛出的BusinessException异常
@ExceptionHandler(value = BusinessException.class)
public ReturnResult defaultErrorHandler(HttpServletRequest req, Exception e) {
ReturnResult result = new ReturnResult(); //出现异常后返回到前端的信息
result.setCode("-1");
result.setMsg("出现了业务异常");
//打印异常信息
logger.error("调用失败", e);
return result;
}
}
1、使用 @ControllerAdvice 注解后,GlobalExceptionHandler 类变为了全局controller异常处理类,即必须与抛出异常的method在同一个controller。
2、 @ExceptionHandler(value = BusinessException.class) 标注在defaultErrorHandler()这个方法上,其中,
@ExceptionHandler 表示这个方法是一个异常处理方法
value=BusinessException.class 表示这个异常处理方法,处理抛出的BusinessException异常
3、异常处理方法内的逻辑:传递给前端 + 服务端自身的打印
ReturnResult result = new ReturnResult(); 表示传递给前端
logger.error(“调用失败”, e); 表示服务端自身的打印
4.3 运行结果
访问/hello资源时被2中捕获BusinessException异常的类捕获,并返回前端数据{“code”:"-1", “message”:“出现业务异常”}, 日志打印信息如下:
五、面试金手指(面试如何回答,面试语言组织)
5.1 起手式,全局异常捕获的两种方式
Spring MVC开发WEB应用时捕获全局异常的方法基本有两种:
- WEB.XML,就是指定error-code和page到指定地址,这也是最传统和常见的做法(一种方式)
- 用Spring的全局异常捕获功能,这种相对可操作性更强一些,可根据自己的需要做一后善后处理,比如日志记录等。(三种方式)
最后加上一种:使用@ResponseBody处理错误。
5.2 五种方式
第一个方式:提供web.xml和applicationContext.xml
web.xml文件中,指定error-code和页面地址location;
applicationContext.xml文件中,指定path和view-name
<error-page
<exception-type java.lang.Exception </exception-type
<location /uncaughtException </location
</error-page
<mvc:view-controller path="/uncaughtException" view-name=“uncaughtException”/>
第二种方式:Spring全局异常,代码侵入方式/Controller增强方式( Advising Controllers)
1、业务逻辑中throw抛出异常
2、异常捕获/异常处理类返回String,表示消息:@ControllerAdvice标注后就是异常处理类, @ExceptionHandler(value = Xxx.class) 表示处理的异常
3、当然,也可以全局异常处理类返回ModelAndView,表示页面:@ControllerAdvice标注后就是异常处理类,@ExceptionHandler(value = Xxx.class) 表示处理的异常
第三种方式:Spring全局异常第二种,xml配置文件方式
1、业务逻辑中throw抛出异常
2、applicationContext.xml配置文件中,配置SimpleMappingExceptionResolver到springioc容器中,对于到跳转页面
第四种方式:Spring全局异常第三种,实现异常捕获接口HandlerExceptionResolver
1、业务逻辑中throw抛出异常
2、实现异常捕获接口HandlerExceptionResolver,重写resolveException()方法,跳转到页面
3、xml配置文件:异常捕获处理类注入到Spring IOC容器
注意:第三种和第四种都是全局配置方式,一样的,只是第三种是在applicationContext.xml中使用HandlerExceptionResolver实现类,使用了系统自带的SimpleMappingExceptionResolver类,第四种自定义HandlerExceptionResolver接口实现类,重写resolveException()方法
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
所以,SimpleMappingExceptionResolver是HandlerExceptionResolver接口的实现类,good
第五种方式:使用Restful的Controller可以使用@ResponseBody处理错误
1、自定义类ErrorInfo
2、一个@ResponseBody返回一个错误实例ErrorInfo实例
六、小结
【Spring】Spring全局异常捕获如何编写(五种方式,三种全局异常处理方式),完成了。