Spring全局异常处理
最近学习Spring时,认识到Spring异常处理的强大。之前处理工程异常,代码中最常见的就是try-catch-finally,有时一个try,多个catch,覆盖了核心业务逻辑:

1 try{
2 ..........
3 }catch(Exception1 e){
4 ..........
5 }catch(Exception2 e){
6 ...........
7 }catch(Exception3 e){
8 ...........
9 }

Spring能够较好的处理这种问题,核心如下,文章主要关注前两个:
- @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
- @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开
- @ResponseStatus:可以将某种异常映射为HTTP状态码
@ExceptionHandler
源码如下:

1 @Target({ElementType.METHOD})
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 public @interface ExceptionHandler {
5 Class<? extends Throwable>[] value() default {};
6 }

该注解作用对象为方法,并且在运行时有效,value()可以指定异常类。由该注解注释的方法可以具有灵活的输入参数(详细参见Spring API):
- 异常参数:包括一般的异常或特定的异常(即自定义异常),如果注解没有指定异常类,会默认进行映射。
- 请求或响应对象 (Servlet API or Portlet API): 你可以选择不同的类型,如ServletRequest/HttpServletRequest或PortleRequest/ActionRequest/RenderRequest
。 - Session对象(Servlet API or Portlet API): HttpSession或PortletSession。
- WebRequest或NativeWebRequest
- Locale
- InputStream/Reader
- OutputStream/Writer
Model
方法返回值可以为:
- ModelAndView对象
- Model对象
- Map对象
- View对象
- String对象
- 还有@ResponseBody、HttpEntity<?>或ResponseEntity<?>,以及void
@ControllerAdvice
源码如下:

1 @Target({ElementType.TYPE})
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 @Component
5 public @interface ControllerAdvice {
6 @AliasFor("basePackages")
7 String[] value() default {};
8
9 @AliasFor("value")
10 String[] basePackages() default {};
11
12 Class<?>[] basePackageClasses() default {};
13
14 Class<?>[] assignableTypes() default {};
15
16 Class<? extends Annotation>[] annotations() default {};
17 }

该注解作用对象为TYPE,包括类、接口和枚举等,在运行时有效,并且可以通过Spring扫描为bean组件。其可以包含由@ExceptionHandler、@InitBinder 和@ModelAttribute标注的方法,可以处理多个Controller类,这样所有控制器的异常可以在一个地方进行处理。
实例
异常类:

1 public class CustomGenericException extends RuntimeException{
2 private static final long serialVersionUID = 1L;
3
4 private String errCode;
5 private String errMsg;
6
7 public String getErrCode() {
8 return errCode;
9 }
10
11 public void setErrCode(String errCode) {
12 this.errCode = errCode;
13 }
14
15 public String getErrMsg() {
16 return errMsg;
17 }
18
19 public void setErrMsg(String errMsg) {
20 this.errMsg = errMsg;
21 }
22
23 public CustomGenericException(String errCode, String errMsg) {
24 this.errCode = errCode;
25 this.errMsg = errMsg;
26 }
27 }

控制器:

1 @Controller
2 @RequestMapping("/exception")
3 public class ExceptionController {
4
5 @RequestMapping(value = "/{type}", method = RequestMethod.GET)
6 public ModelAndView getPages(@PathVariable(value = "type") String type) throws Exception{
7 if ("error".equals(type)) {
8 // 由handleCustomException处理
9 throw new CustomGenericException("E888", "This is custom message");
10 } else if ("io-error".equals(type)) {
11 // 由handleAllException处理
12 throw new IOException();
13 } else {
14 return new ModelAndView("index").addObject("msg", type);
15 }
16 }
17 }

异常处理类:

1 @ControllerAdvice
2 public class ExceptionsHandler {
3
4 @ExceptionHandler(CustomGenericException.class)//可以直接写@ExceptionHandler,不指明异常类,会自动映射
5 public ModelAndView customGenericExceptionHnadler(CustomGenericException exception){ //还可以声明接收其他任意参数
6 ModelAndView modelAndView = new ModelAndView("generic_error");
7 modelAndView.addObject("errCode",exception.getErrCode());
8 modelAndView.addObject("errMsg",exception.getErrMsg());
9 return modelAndView;
10 }
11
12 @ExceptionHandler(Exception.class)//可以直接写@EceptionHandler,IOExeption继承于Exception
13 public ModelAndView allExceptionHandler(Exception exception){
14 ModelAndView modelAndView = new ModelAndView("generic_error");
15 modelAndView.addObject("errMsg", "this is Exception.class");
16 return modelAndView;
17 }
18 }

JSP页面:
正常页面index.jsp:

1 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
2 <html>
3 <body>
4 <h2>Spring MVC @ExceptionHandler Example</h2>
5
6 <c:if test="${not empty msg}">
7 <h2>${msg}</h2>
8 </c:if>
9
10 </body>
11 </html>

异常处理页面generic_error.jsp

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body> <c:if test="${not empty errCode}">
<h1>${errCode} : System Errors</h1>
</c:if> <c:if test="${empty errCode}">
<h1>System Errors</h1>
</c:if> <c:if test="${not empty errMsg}">
<h2>${errMsg}</h2>
</c:if> </body>
</html>

测试运行如下:
正常情况:

CustomGenericException异常情况:

IOException异常情况:

总结
- @ExceptionHandler和@ControllerAdvice能够集中异常,使异常处理与业务逻辑分离
- 本文重点理解两种注解方式的使用
参考:
- Spring API:http://docs.spring.io/spring/docs/current/javadoc-api/
- 《Spring in Action》
- Spring MVC @ExceptionHandler Example
Spring全局异常处理的更多相关文章
- Spring 全局异常处理
[参考文章]:Spring全局异常处理的三种方式 [参考文章]:Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理 [参考文章]:@ControllerAdvic ...
- 《Spring全局异常处理》从零掌握@ControllerAdvice注解
一.开门见山 在前后端分离框架的大趋势下,前后端基本的职责已经确定. 前端主要负责界面的处理以及基本的判空检验.数据来源则通过vue调用后端发布的接口. 后端的原型还是mvc的模式: controll ...
- Spring全局异常处理的三种方式
在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作 ...
- spring全局异常处理 自定义返回数据结构
在写api接口中,正常返回和异常错误返回我们都希望很清楚的将这些信息清楚的返回给用户,出现异常情况下需要清楚的知道是参数异常还是未知异常,而不是返回一个不正确的数据结构. 所以此处只针对写api接口时 ...
- Spring MVC 解决无法访问静态文件和"全局异常处理"
我们都知道,Spring MVC的请求都会去找controller控制器,若果我们页面中引入了一个外部样式,这样是没效果的, 我们引入样式的时候是通过<like href="...&q ...
- Spring Boot 2.x 系列教程:WebFlux REST API 全局异常处理 Error Handling
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本文内容 为什么要全局异常处理? WebFlux REST 全 ...
- Spring Boot 全局异常处理
Spring Boot版本 1.5 @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExcept ...
- Spring中通过java的@Valid注解和@ControllerAdvice实现全局异常处理。
通过java原生的@Valid注解和spring的@ControllerAdvice和@ExceptionHandler实现全局异常处理的方法: controller中加入@Valid注解: @Req ...
- Spring Boot 2.X(十一):全局异常处理
前言 在 Java Web 系统开发中,不管是 Controller 层.Service 层还是 Dao 层,都有可能抛出异常.如果在每个方法中加上各种 try catch 的异常处理代码,那样会使代 ...
随机推荐
- nginx 自签名证书 配置 https
最近在研究nginx,整好遇到一个需求就是希望服务器与客户端之间传输内容是加密的,防止中间监听泄露信息,但是去证书服务商那边申请证书又不合算,因为访问服务器的都是内部人士,所以自己给自己颁发证书,忽略 ...
- 【python】python实例集<一>
#打开一个记事本 import os os.startfile('notepad.exe') #当前文件的根目录 import os print os.path.join(os.path.dirnam ...
- [经验]PLSQL乱码解决
本文摘自:http://jingyan.baidu.com/article/36d6ed1f2861f41bcf488327.html @echo off set path=D:\Program Fi ...
- Splunk安装部署基础篇
Splunk安装(以4.3.4版本为例) 下载splunk软件包,并解压,直接启动即可. --Linux-x86_64.tgz -C /optcd /opt/splunk/bin./splunk st ...
- mysql用一个表更新另一个表的方法
Solution 1: 修改1列(navicate可行) update student s, city c set s.city_name = c.name where s.city_code = ...
- Spring中使用JDBC
Spring中的数据库异常体系 使用JDBC(不使用Spring)的时候,我们需要强制捕获SQLException,否则无法使用JDBC处理任何事情.SQLException表示尝试访问数据库的时候出 ...
- java 工具类 static
http://wenwen.sogou.com/z/q526955586.htm 工具类其实就是提供了一些公共的函数,方便其他类去调用,函数都写成static一方面是因为方便,另一方面是工具类的函数是 ...
- uva-11044-水题
#include <iostream> #include<memory.h> #include<stdio.h> using namespace std; int ...
- 概率分布之间的距离度量以及python实现
1. 欧氏距离(Euclidean Distance) 欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式.(1)二维平面上两点a(x1,y1)与b(x2,y2)间的欧 ...
- 使用python读取大文件
python中读取数据的时候有几种方法,无非是read,readline,readlings和xreadlines几种方法,在几种方法中,read和xreadlines可以作为迭代器使用,从而在读取大 ...