异常处理器
异常处理器
问题导入
问题1:项目各个个层级均可能出现异常,异常处理代码书写在哪一层?
异常介绍
- 程序开发过程中不可避免的会遇到异常现象,我们不能让用户看到这样的页面数据
出现异常现象的常见位置与常见诱因如下:
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
异常处理器
编写异常处理器
异常处理器的底层原理,就是AoP。
@RestControllerAdvice是对Controller进行增强的,可以全局捕获spring mvc抛的异常。并匹配相应的@ExceptionHandler中指定的异常类型,重新封装异常信息,将统一格式返回给前端。
这个类所在的包要能够被Spring MVC扫描到
package com.tyhxzy.controller;
import com.tyhxzy.entity.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerExceptionResolver;
/**
* 统一异常处理器:
* 作用:一旦发现controller抛出异常,就会去处理。
* 定义统一异常处理器步骤:
* 1.该类使用@ControllerAdvice注解,表示该类是一个异常处理器
* 2.
*/
@ControllerAdvice //controller\RestController 出现异常可以去处理
//@RestControllerAdvice //只能处理Recontroller 出现异常,返回值默认就是json
public class ProjectExceptionHandler {
//@ExceptionHandler 处理的是那种类型的异常
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handlerException(Exception e){
e.printStackTrace();
return new Result(4444,null,"您的网络异常,请稍后!");
}
}
注意: springConfig类需要扫描exception包
使用异常处理器之后的效果
@RestControllerAdvice注解介绍
- 名称:@RestControllerAdvice
- 类型:类注解
- 说明:此注解自带@ResponseBody注解与@Component注解,具备对应的功能
@ExceptionHandler注解介绍
- 名称:@ExceptionHandler
- 类型:方法注解
- 位置:专用于异常处理的控制器方法上方
- 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
- 说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
小结
如何定义异常处理器:
1. 自定义一个类,在类上添加@ControllerAdvice或者@RestControllerAdvice注解。
2. 定义一个方法,在方法上@ExceptionHandler指定处理那种类型的异常。
项目异常处理方案
问题导入
请说出项目当前异常的分类以及对应类型异常该如何处理?
项目异常分类
- 业务异常(BusinessException) /books/1
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
- 其他异常(Exception)
- 编程人员未预期到的异常
项目异常处理方案
- 业务异常(BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护(纳入预期范围内)
- 记录日志
项目异常处理代码实现
根据异常分类自定义异常类
自定义项目系统级异常
package com.tyhxzy.exceptions;
import lombok.Data;
/**
* 异常异常,用户的操作不规范导致的。
*
* 如何自定义一个异常:
*/
@Data
public class SystemException extends RuntimeException {
private Integer code; //表示是什么类型的异常
public SystemException(Integer code) {
this.code = code;
}
public SystemException(Integer code,String message) {
super(message);
this.code = code;
}
}
自定义项目业务级异常
package com.tyhxzy.exceptions;
/**
* 业务异常,用户的操作不规范导致的。
*
* 如何自定义一个异常:
*/
@Data
public class BusinessException extends RuntimeException {
private Integer code; //表示是什么类型的异常
public BusinessException(Integer code) {
this.code = code;
}
public BusinessException(Integer code,String message) {
super(message);
this.code = code;
}
}
自定义异常编码(持续补充)
package com.tyhxzy.entity;
public interface Code {
//接口成员变量默认的修饰符 public static final
Integer SAVE_OK = 20011;
Integer DELETE_OK = 20021;
Integer UPDATE_OK = 20031;
Integer GET_OK = 20041;
Integer SAVE_ERR = 20010;
Integer DELETE_ERR = 20020;
Integer UPDATE_ERR = 20030;
Integer GET_ERR = 20040;
//异常的状态码(新增)
Integer SYSTEM_ERR = 50001;
Integer SYSTEM_TIMEOUT_ERR = 50002;
Integer SYSTEM_UNKNOW_ERR = 59999;
Integer BUSINESS_ERR = 60002;
}
触发自定义异常
package com.tyhxzy.controller;
import com.tyhxzy.entity.Book;
import com.tyhxzy.entity.Code;
import com.tyhxzy.entity.Result;
import com.tyhxzy.exceptions.BusinessException;
import com.tyhxzy.exceptions.SystemException;
import com.tyhxzy.service.BookService;
import org.apache.ibatis.annotations.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller //把该类变成一个controller
@RestController //@RestController作用: 1. 会把该类变成contrller, 2. 所有方法的返回值都是json
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
/**
* 查询单个图书
* restful风格对于查询使用get的请求方式
* @param id
* @return
*/
@GetMapping("/{id}")
public Result findById(@PathVariable("id") Integer id){
if(id<0){
throw new BusinessException(Code.BUSINESS_ERR,"数据有误");
}
/* //模拟数据库服务器宕机
if(true){
throw new SystemException(Code.SYSTEM_ERR,"数据库宕机");
}*/
Book book = bookService.getById(id);
if(book!=null){
return new Result(Code.GET_OK,book,"查询成功");
}else{
return new Result(Code.GET_ERR,book,"查询失败");
}
}
}
在异常通知类中拦截并处理异常
分别写三个方法用来捕获每种异常
package com.tyhxzy.controller;
import com.tyhxzy.entity.Result;
import com.tyhxzy.exceptions.BusinessException;
import com.tyhxzy.exceptions.SystemException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerExceptionResolver;
/**
* 统一异常处理器:
* 作用:一旦发现controller抛出异常,就会去处理。
* 定义统一异常处理器步骤:
* 1.该类使用@ControllerAdvice注解,表示该类是一个异常处理器
* 2.
*/
@ControllerAdvice //controller\RestController 出现异常可以去处理
//@RestControllerAdvice //只能处理Recontroller 出现异常,返回值默认就是json
public class ProjectExceptionHandler {
@ExceptionHandler(SystemException.class)
@ResponseBody
public Result handlerSystemException(SystemException e){
e.printStackTrace();
return new Result(e.getCode(),null,e.getMessage());
}
@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result handlerBusinessException(BusinessException e){
e.printStackTrace();
return new Result(e.getCode(),null,e.getMessage());
}
//@ExceptionHandler 处理的是那种类型的异常
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handlerException(Exception e){
e.printStackTrace();
return new Result(4444,null,"您的网络异常,请稍后!");
}
}
测试:在postman中发送请求访问getById方法,传递参数-1,得到以下结果:
小结
1. 不同类型的异常处理方式其实不一样。
2. 统一异常处理器可以处理不同类型的异常。
文件上传的必要前提:
1、form 表单的 enctype 取值必须是:multipart/form-data(默认值是:application/x-www-form-urlencoded)enctype:是表单请求正文的类型
2、 method 属性取值必须是 Post
3、提供一个文件选择域<input type=”file”/>
文件上传原理分析
当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。
enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是:key=value&key=value&key=value;
当 form 表单的 enctype 取值为 Mutilpart/form-data 时,请求正文内容就变成:每一部分都是 MIME 类型描述的正文;
SpringMVC的文件上传
构建maven工程添加相关依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
编写 jsp 页面
<form enctype="multipart/form-data" method="post" action="/file/fileUpload">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
编写控制器
@RequestMapping("fileUpload")
public String fileUpload(MultipartFile file){
File dest = new File("C:\\Users\\mwx\\Pictures\\"+file.getOriginalFilename());
//文件上传
try {
file.transferTo(dest);
} catch (IOException e) {
e.printStackTrace();
}
return "main";
}
配置文件解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>