springboot 全局异常处理实例 – @ControllerAdvice + @ExceptionHandler()
为什么要使用全局异常处理?
1.一方面,在编写代码时,会不经常的出现 try {} catch {} 等异常处理的代码块,当然对于小量代码来讲,在程序中添加异常处理块,没有任何的不妥。而当程序成基数的增长,一个系统里面或许或有成百上千的类,那么这个try {} catch {} 也是基数增长的,那么程序就得非常繁杂,降低了代码的可阅读行。那么可以如同通用类一般,我们将其提取出来,构建成一个层面。那么,这便是非常不错的。
2.另一方面,在大量代码的系统中,再编写新模块时,也要进行异常处理。如果我们将其集中提取出来,我们便只用关心逻辑代码如何编写,而不用因为异常导致出现错误。而且,这仅仅是新增模块,我们仔细点可以随着代码的编写去处理异常,那么,如果是修改模块呢?有的代码是要动的,有的却是要保留的,那么,这便会一不小心就会忘掉某个异常处理。说白点,异常处理就是方便后期维护和管理。
本文讲解 @ControllerAdvice + @ExceptionHandler() 全局处理异常,对于程序的设计,只要设计得当,就再也不用在 MVC 架构层进行 try-catch 了!
如果操作?
这是一个非常干净的 Springboot 项目!开始操作,既然是全局处理异常,肯定是要编写接口以及业务层的。
先写一个简单接口!抛出异常!
package com.wang.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("hello")
public String hello() throws Exception {
int a = 0;
return a + "";
}
}
启动,测试:
成功!再此,我们对接口抛出异常!再次启动测试
package com.wang.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("hello")
public String hello() throws Exception {
int a = 0;
try {
a = 1/0;
} catch (Exception e) {
throw new ArithmeticException("算术 Exception !");
}
return a + "";
}
}
我们可以清楚的看到这边是抛出了一个异常。
我们加上 @ControllerAdvice + @ExceptionHandler() 做的全局异常处理!
package com.wang.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public String handleException() {
return "这里是全局异常处理错误!";
}
}
注意上述中,只是添加了一个异常处理程序。再次运行代码!
可以看出,我们的异常被异常处理程序捕获并处理!并且,还可以跳转到错误页面。
再 templates 中添加一个 error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>error</title>
</head>
<body>
<h1>出现了全局异常!这里是异常页面!</h1>
</body>
</html>
页面也是十分简单,当然,跳转到 html 页面,springboot 还需要引入 thymeleaf 模板引擎!
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
对于异常处理类的更改:
package com.wang.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException() {
return "error";
}
}
再次运行代码,这里一定要导入模板引擎,不然出现错误:
成功!我们在对 Service 层进行处理异常,编写代码。
Controller层:
package com.wang.controller;
import com.wang.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@Autowired
HelloService helloService;
@ResponseBody
@RequestMapping("hello")
public String hello() throws Exception {
int a = 0;
try {
a = 1/0;
} catch (Exception e) {
throw new ArithmeticException("算术 Exception !");
}
return a + "";
}
@RequestMapping("hello1")
@ResponseBody
public String helloService() throws Exception {
return helloService.HelloService(false);
}
}
Service层:
package com.wang.service;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
public class HelloService {
public String HelloService(boolean flag) throws Exception {
if(flag) {
throw new IOException();
}
return "这里是Service层!";
}
}
异常处理类:
package com.wang.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException() {
return "error";
}
@ExceptionHandler(IOException.class)
@ResponseBody
public String IOException() {
return "这是出现了 IOException 才能出现的错误!";
}
}
在程序中,我们可以发现,当 controller 调用 service 层时,传入参数为 fasle 时,返回正常值,当传入值时 true 时,抛出 IOException 异常!
先传入参数 false 运行程序:
我这里8080端口被占用,用8898端口进行访问。
可以带到运行正确。改变参数 true ,让其抛出异常进行测试。
被异常处理类进行处理。
总结
在全局异常处理中,全局异常处理类利用 aop 的思想,一旦你的Service,Controller层发生错误,便会被捕获处理,使请求返回时走异常处理类。在全局异常处理类中,可以将异常进行细化的切分,使不用的异常被不用的异常处理方法所处理,并且在最后在进行 Exception 进行异常处理,起一个兜底的作用,防止有未考虑的异常而发生意外。
这不就是个try {} catch {} 的思想所吻合吗?而这做了一个宏大的异常提取。
try {
...
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
} catch (IOFileUploadException e) {
e.printStackTrace();
} catch (ArithmeticException e) {
e.printStackTrace();
} ... {
} catch (Exception e) {
e.printStackTrace();
} finally {
...
}
上述便是不同异常被不同异常机制捕获处理,最后的 Exception 便起一个兜底的作用!
OK ! 全局异常机制便做了一个简单的了解。
欢迎留言!