思考1:
思考2:
其实,spring已经早早就帮我们解决好处理异常的问题了,不用我们再写AOP代码了,但是我们要按照spring处理异常的格式进行书写
模拟一下前端发送访问资源请求后,收到的是异常信息(此时后端还没有做异常处理器):
前端访问增加功能的资源路径后,收到的信息如下:(这时前端就懵逼了,我TM访问的是增加功能,这tm返回的是什么东西~)
因此我们后端就需要做异常处理了(这里演示的就是其他异常,不需要向系统异常和业务异常一样还需要创建个类,用来封装提示用户信息到属性中):
后端处理完异常后客户端再次访问增加功能资源后,会接收到什么信息:
因此客户端又事B了说,我什么数据都没有接收啊,我这是咋回事啊,
因此我们后端在处理完异常之后,可以返回一些数据给前端用户,那么用户就知道了,哦原来是后端出异常了啊~
前端用户接收的信息如下所示:
项目中出现的不同种类异常形式:
那么如何处理上面三种类型的异常呢:
代码演示如下所示:
第一步:先把系统异常类、业务异常类写出来:(实际开发中业务中只出现系统异常的会,那么就写一个系统异常类就可以了,我这里是把三种情况(系统异常、业务异常、其他异常都写出来了),实际开发中出现哪个异常就写哪个异常类,然后通过异常处理器进行处理并且返回给用户提示信息就可以了)
系统异常类:
- package com.itheima.exception;
-
- /**
- * 业务异常类
- *
- * 注:继承RuntimeException运行时异常类的目的就是运行时出现异常的时候不再手动往上抛了,自动帮我们往上抛
- *
- */
- public class SystemException extends RuntimeException{
-
- private Integer code; // 加个code编号属性,来标注以后出现的是哪一种异常
-
- /**
- * 把构造方法最好都写出来
- */
-
- public SystemException(Integer code) {
- this.code = code;
- }
-
- public SystemException(String message, Integer code) {
- super(message);
- this.code = code;
- }
-
- public SystemException(String message, Throwable cause, Integer code) {
- super(message, cause);
- this.code = code;
- }
-
- public SystemException(Throwable cause, Integer code) {
- super(cause);
- this.code = code;
- }
-
- public SystemException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Integer code) {
- super(message, cause, enableSuppression, writableStackTrace);
- this.code = code;
- }
-
- /**
- * getter and setter方法
- */
- public Integer getCode() {
- return code;
- }
-
- public void setCode(Integer code) {
- this.code = code;
- }
- }
业务异常类:
- package com.itheima.exception;
-
- /**
- * 系统异常类
- *
- * 注:继承RuntimeException运行时异常类的目的就是运行时出现异常的时候不再手动往上抛了,自动帮我们往上抛
- *
- */
- public class BusinessException extends RuntimeException{
-
- private Integer code; // 加个code编号属性,来用状态码标注以后出现的是哪一种异常
-
- /**
- * 把构造方法最好都写出来
- */
-
-
- public BusinessException(Integer code) {
- this.code = code;
- }
-
- public BusinessException(String message, Integer code) {
- super(message);
- this.code = code;
- }
-
- public BusinessException(String message, Throwable cause, Integer code) {
- super(message, cause);
- this.code = code;
- }
-
- public BusinessException(Throwable cause, Integer code) {
- super(cause);
- this.code = code;
- }
-
- public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Integer code) {
- super(message, cause, enableSuppression, writableStackTrace);
- this.code = code;
- }
-
- /**
- * getter and setter方法
- */
- public Integer getCode() {
- return code;
- }
-
- public void setCode(Integer code) {
- this.code = code;
- }
- }
第二步:假设业务端通过id查询功能的时候出现了系统异常(比如说:服务器宕机了):
那么就把系统异常捕获到后,把想要返回给前端用户的信息封装到系统异常类的属性当中(通过构造方法),然后通过异常处理器返回给前端用户信息
注意1:假设系统异常和业务异常都出现在了业务层的某个功能中,那么先捕获到哪个种类的异常,异常处理器就会获取到该异常信息,并且把提示数据返回提示给用户(下面是先捕获的系统异常,因为系统异常排第一)
- package com.itheima.service.impl;
-
- import com.itheima.controller.Code;
- import com.itheima.dao.BookDao;
- import com.itheima.domain.Book;
- import com.itheima.exception.BusinessException;
- import com.itheima.exception.SystemException;
- import com.itheima.service.BookService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import java.util.List;
-
-
- @Service
- public class BookServiceImpl implements BookService {
-
- /**
- * 注入dao数据层依赖 : private BookDao bookDao;
- * 然后等会spring容器拿到管理的业务层BookServiceImpl对象后,又因为业务层和数据层有依赖关系
- * 通过依赖关系就可以调用数据层的增删改查功能了
- */
-
- @Autowired // 自动装配(处理依赖关系)注解
- private BookDao bookDao;
-
- // 增
- public boolean save(Book book) {
-
- // 调用数据层的增加功能
- bookDao.save(book);
- return true;
- }
-
- // 删
- public boolean delete(Integer id) {
-
- // 调用数据层的删除功能
- bookDao.delete(id);
- return true;
- }
-
- // 改
- public boolean update(Book book) {
-
- // 调用数据层的修改功能
- bookDao.update(book);
- return true;
- }
-
- // 通过id查询
- public Book getById(Integer id) {
-
- /**
- * 模拟假定这个地方出现了 系统异常(比如说:程序运行到这里后服务器宕机了)
- *
- * 我们用 1/0来模拟:服务器宕机
- *
- */
- try {
- int i =1/0; // 模拟系统异常
- } catch (Exception e){ // 捕获到系统异常后,把想要返回给前端的信息封装到
- // 系统异常类的属性中(通过构造方法)
- throw new SystemException("服务器访问超时~",e, Code.SYSTEM_ERR);
- // 把想返回给前端的信息封装到SystemException对象的属性当中
- // Code.SYSTEM_ERR :系统异常状态码标识 50001
- }
-
- /**
- * 这里模拟假设出现了 业务异常
- */
-
- if (id==2){
- throw new BusinessException("请不要用你的技术挑战我的耐性~",Code.BUSINESS_ERR);
- // Code.BUSINESS_ERR : 业务异常状态码标识 60001
- }
-
- // 调用数据层的通过id查询功能
- Book books = bookDao.getById(id);
- return books;
- }
-
- // 查询所有
- public List
getAll() { -
- // 调用数据层的查询所有功能
- List
list = bookDao.getAll(); - return list;
- }
- }
第三步:通过异常处理器进行系统或者业务或者其他异常处理并且把刚才封装到系统异常类/业务异常类属性中返回给客户的数据,返回给用户:
细节:真正开发中,我们其实可以直接把这三种异常处理器写出来,然后项目中如果出现的是系统异常,那么就直接被系统异常处理器处理并且返回给用户信息即可了,如果是其他两种,也可以直接被其他两种异常处理器捕获到然后进行处理异常
- package com.itheima.controller;
-
- import com.itheima.exception.BusinessException;
- import com.itheima.exception.SystemException;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.RestControllerAdvice;
-
- /**
- * spring处理异常 (异常处理器类)
- */
-
- // @ControllerAdvice // 这是普通风格处理异常的注解 (只要访问路径用的不是REST风格就用这个注解)
- @RestControllerAdvice // 这是REST风格的处理异常的注解 (告诉spring这是一个处理异常的类的注解)
- // 注1:这个@RestControllerAdvice注解需要被SpringMvcConfig类的@ComponentScan({"com.itheima.controller"})注解
- // 扫描到,(SpringMvcConfig类的扫描注解刚好扫到controller包下,所以能扫到,因此如果换成其他的包,记得扫一下)
-
- public class ProjectExceptionAdvice {
-
- /**
- * 注: 系统异常处理器
- *
- * @return: 返回给前端信息
- */
-
- @ExceptionHandler(SystemException.class) // 该注解作用: 拦截是否是系统异常,是的话就调用下面的方法进行处理异常
- public Result doSystemException(SystemException sy){
- // (SystemException sy):把SystemException对象传递过来(因为刚才业务层模拟系统异常的时候,把返回给前端的信息封装到sy对象属性中了)
- /**
- * 拿到系统异常之后需要做以下几步骤:
- * 1、记录日志
- * 2、发送给运维,开发人员
- * 3、安抚一下客户(客户访问资源的时候出现异常了,咱们肯定要说一些好听的给用户)
- */
-
- return new Result(null,sy.getCode(), sy.getMessage());
- // 把封装到sy系统异常类属性中的系统异常状态码信息返回给用户
- }
-
- /**
- * 注: 业务异常处理器 (和系统异常处理器方式是一样的)
- *
- * @return: 返回给前端信息
- */
-
- @ExceptionHandler(BusinessException.class) // 拦截是否是业务异常,是的话就调用下面的方法进行处理异常
- public Result doBusinessException(BusinessException bs){
-
-
- return new Result(null,bs.getCode(),bs.getMessage());
- }
-
- /**
- * 注: 其他异常处理器 (可以理解为除了业务异常和系统异常之外的所有异常)
- */
-
- @ExceptionHandler(Exception.class) // 拦截是否是其他异常,是的话就调用下面的方法进行处理异常
- public Result doException(){
-
- return new Result(null,0,"出现异常了铁子~");
- }
- }
假设客户端进行访问有系统异常的通过id查询的功能时候:
补:Code类:专门用来储存成功/失败标识符号的
- package com.itheima.controller;
-
-
- /**
- * 分析该类的作用:
- * 标记状态码(比如1:表示成功,2:表示失败)的类,( Result里面的 )
- *
- * 注意:这些状态码不是固定的,是实际开发中前端程序员和后端程序员商量好规定的一种状态信息,
- * 通过这些商量的状态码,就可以知道拿没拿到数据了
- *
- */
- public class Code {
-
- /**
- * 调用增删改查功能成功的状态码标记
- *
- * 注:因为是static静态修饰的,所以可以直接用类名Code调用属性名
- * public修饰:代表公共的(也就是说,任何包下的只要通过类名. 的方式都可以调取到Code类下的属性值)
- *
- */
- public static final Integer SAVE_OK = 20011;
- public static final Integer DELETE_OK = 20021;
- public static final Integer UPDATE_OK = 20031;
- public static final Integer SELECT_OK = 20041;
-
- /**
- * 调用增删改查功能失败的状态码标记
- */
- public static final Integer SAVE_ERR = 20010;
- public static final Integer DELETE_ERR = 20020;
- public static final Integer UPDATE_ERR = 20030;
- public static final Integer SELECT_ERR = 20040;
-
-
- /**
- * 系统异常 & 业务异常成功/失败状态码标记
- */
- public static final Integer SYSTEM_ERR =50001; // 出现系统异常状态码
- public static final Integer BUSINESS_ERR =60001; // 出现业务异常状态码
-
-
- }