# SSM 整合
# SSM 整合流程
1. 创建工程
2.SSM 整合
Spring
- SpringConfig
MyBatis
- MybatisConfig
- JdbcConfig
- jdbc.properties
SpringMvc
- ServletConfig
- SpringMvcConfig
3. 功能模块
表与实体类
dao (接口 + 自动代理)
service (接口 + 实现类)
- 业务层接口测试 (整合 JUnit)
controller
- 表现层接口测试 (PostMan)
事务处理
# 表现层数据封装
前端接收数据格式
增删改
- 查单条
- 查全部
。。。
每种操作对应不同的格式,以后说不定还有更多的格式于是就想了一个方案
创建结构模型类,封装数据到 data 属性中
增删改
- 查单条
- 查全部
- 但是不知道执行的操作是什么
于是乎:封装操作结果到 code 属性中
增删改
- 查单条
- 查全部
但是查询出来的结果怎么看呢只知道查询的 code 编号是 20041
于是乎:封装特殊信息到 message (msg) 属性中
20041 表示成功,20040 表示失败,用数据的最后一位的 0 和 1 来表示
查询单条
- 设置统一数据返回结果类
public class Result{ | |
private Object data; | |
private Integer code; | |
private String msg; | |
} |
注意事项:
Result 类中的字段并不是固定的,可以根据需要自行增减
提供若干个构造方法,方便操作
<span alt="wavy" style="color:red"> 需要生成 getter setter 代码否则会报错 </span>.
- 设置统一数据返回结果编码
@SuppressWarnings("all") | |
public class Code { | |
public static final Integer SAVE_OK = 20011; | |
public static final Integer DELETE_OK = 20011; | |
public static final Integer UPDATE_OK = 20011; | |
public static final Integer SELECT_OK = 20011; | |
public static final Integer SAVE_ERR = 20010; | |
public static final Integer DELETE_ERR = 20010; | |
public static final Integer UPDATE_ERR = 20010; | |
public static final Integer SELECT_ERR = 20010; | |
} |
注意事项:
Code 类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为 GET_OK,GET_ALL_OK,GET_PAGE_OK
- 根据情况设定合理的 Result
@SuppressWarnings("all") | |
@RestController | |
@RequestMapping("/books") | |
public class BookController { | |
@Autowired | |
private BookService bookService; | |
@PostMapping | |
public Result save(@RequestBody Book b) { | |
boolean save = bookService.save(b); | |
return new Result(save ? Code.SAVE_OK:Code.SAVE_ERR,save); | |
} | |
@PutMapping | |
public Result update(@RequestBody Book b) { | |
boolean update = bookService.update(b); | |
return new Result(update ? Code.UPDATE_OK:Code.UPDATE_ERR,update); | |
} | |
@DeleteMapping("/{id}") | |
public Result delete(@PathVariable Integer id) { | |
boolean delete = bookService.delete(id); | |
return new Result(delete ? Code.DELETE_OK:Code.DELETE_ERR,delete); | |
} | |
@GetMapping("/{id}") | |
public Result getById(@PathVariable Integer id) { | |
Book byId = bookService.getById(id); | |
Integer i = byId != null ? Code.SELECT_OK:Code.SELECT_ERR; | |
String msg = byId != null ? "查询成功":"查询失败"; | |
return new Result(i,byId,msg); | |
} | |
@GetMapping | |
public Result getAll() { | |
List<Book> all = bookService.getAll(); | |
Integer i = all != null ? Code.SELECT_OK:Code.SELECT_ERR; | |
String msg = all != null ? "查询成功":"查询失败"; | |
return new Result(i,all,msg); | |
} | |
} |
send request
# 异常处理器
- 程序开发过程中不可避免的会遇到异常现象
出现异常现象的常见位置与常见诱因如下:
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致 (例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致 (例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据手机,校验等规则导致 (例如:不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致 (例如:必要释放的连接长期未释放等)
各个层级均出现异常,异常处理代码书写在哪一层
- <font style="color:red"> 所有的异常均抛出到表现层进行处理 </font>
- 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不大,如何解决?
- AOP 思想
基于以上的特征我们发现:
- 首先异常要分类处理 (不同种类的异常处理方式是不一样的)
- 异常要放到表现层处理
- 异常要使用 AOP 的思想来处理
SpringMvc 给我们提供了快速的处理方案
异常处理器
- 集中的,统一的处理项目中出现的异常
@SuppressWarnings("all") | |
@RestControllerAdvice | |
public class ProjectExceptionAdvice { | |
@ExceptionHandler(Exception.class) | |
public Result doException(Exception e){ | |
System.out.println("异常:你干嘛~ 嗨嗨呦,啊哈哈~"); | |
return new Result(666,null,"异常:你干嘛~ 嗨嗨呦,啊哈哈~"); | |
} | |
} |
# @RestControllerAdvice
类型:类注解
位置:Rest 风格开发的控制器增强类定义上方
作用:为 Rest 风格开发的控制器类做增强
说明:
- 此注解自带 @ResponseBody 注解与 @Component 注解,具备对应的功能
# @ExceptionHandler
类型:方法注解
位置:专用于异常处理的控制器方法上方
作用:设置指定异常的处理方案,功能等同与控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
说明:
- 此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
- 异常处理器处理效果比对
Before
After
# 项目异常处理方案
- 项目异常分类
- 不规范的用户行为操作产生的异常
规范的用户行为产生的异常
或者用户直接在 age 字段中输入的 haha 那么程序直接可能就异常了
这是用户行为,还有非用户行为比如说数据库垮掉了,这种异常可预计但不可避免
编程人员未预期到的异常
项目异常分类
- 业务异常 (BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常 (SystemException)
- 项目运行过程中可预计且无法避免的异常
- 其它异常 (Exception)
- 编程人员未预期到的异常
- 业务异常 (BusinessException)
项目异常处理方案
- 业务异常 (BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常 (SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其它异常 (Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护 (纳入预期范围内)
- 记录日志
- 业务异常 (BusinessException)
# 步骤
- 自定义项目系统级异常
@SuppressWarnings("all") | |
public class SystemException extends RuntimeException{ | |
// 异常编码 | |
private Integer code; | |
public SystemException( Integer code, String message) { | |
super(message); | |
this.code = code; | |
} | |
public SystemException(Integer code, String message, Throwable cause) { | |
super(message, cause); | |
this.code = code; | |
} | |
public Integer getCode() { | |
return code; | |
} | |
public void setCode(Integer code) { | |
this.code = code; | |
} | |
} |
- 自定义项目业务级异常
@SuppressWarnings("all") | |
public class BusinessException extends RuntimeException{ | |
// 异常编码 | |
private Integer code; | |
public BusinessException(Integer code, String message) { | |
super(message); | |
this.code = code; | |
} | |
public BusinessException(Integer code, String message, Throwable cause) { | |
super(message, cause); | |
this.code = code; | |
} | |
public Integer getCode() { | |
return code; | |
} | |
public void setCode(Integer code) { | |
this.code = code; | |
} | |
} |
- 自定义异常编码 (持续补充)
@SuppressWarnings("all") | |
public class Code { | |
public static final Integer SYSTEM_ERR = 50011; | |
public static final Integer BUSINESS_ERR = 50012; | |
public static final Integer SYSTEM_TIMEOUT_ERR = 50013; | |
public static final Integer SYSTEM_UNKNO_ERR = 5999; | |
} |
- 触发自定义异常
@SuppressWarnings("all") | |
@Service | |
public class BookServiceImpl implements BookService { | |
@Autowired | |
private BookDao bookDao; | |
@Override | |
public Book getById(Integer id) { | |
if(id == 1){ | |
throw new BusinessException(Code.BUSINESS_ERR,"请输入规范的信息"); | |
} | |
// 将可能出现的异常进行包装,转换成自定义异常 | |
try{ | |
int i = 1/0; | |
}catch(ArithmeticException ex){ | |
throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请稍后再试。。。",ex); | |
} | |
return bookDao.getById(id); | |
} | |
} |
- 拦截并处理异常
@SuppressWarnings("all") | |
@RestControllerAdvice | |
public class ProjectExceptionAdvice { | |
@ExceptionHandler(Exception.class) | |
public Result doException(Exception e){ | |
// 记录日志 | |
// 发送消息给运维 | |
// 发送邮件给开发人员,异常对象发送给开发人员 | |
return new Result(Code.SYSTEM_UNKNO_ERR,null,"异常:你干嘛~ 嗨嗨呦,啊哈哈~"); | |
} | |
@ExceptionHandler(SystemException.class) | |
public Result doSystemException(SystemException e){ | |
// 记录日志 | |
// 发送消息给运维 | |
// 发送邮件给开发人员,异常对象发送给开发人员 | |
return new Result(e.getCode(),null,e.getMessage()); | |
} | |
@ExceptionHandler(BusinessException.class) | |
public Result doBusinessException(BusinessException e){ | |
return new Result(e.getCode(),null,e.getMessage()); | |
} | |
} |
- 异常处理器效果对比
# 另一种方式 (推荐,结合上述)
定义一个异常处理类
package com.atguigu.gulimail.product.exception; | |
import com.atguigu.common.utils.R; | |
import lombok.extern.slf4j.Slf4j; | |
import org.springframework.validation.BindingResult; | |
import org.springframework.web.bind.MethodArgumentNotValidException; | |
import org.springframework.web.bind.annotation.ExceptionHandler; | |
import org.springframework.web.bind.annotation.ResponseBody; | |
import org.springframework.web.bind.annotation.RestControllerAdvice; | |
import java.util.HashMap; | |
import java.util.Map; | |
/** | |
* 集中处理所有异常 | |
*/ | |
@Slf4j | |
@RestControllerAdvice(basePackages = "com.atguigu.gulimail.product.controller") | |
public class GulimallExceptionControllerAdvice | |
{ | |
@ResponseBody | |
// 一开始不知道这个错误的异常类是什么先定义 Exception.class | |
@ExceptionHandler(value = MethodArgumentNotValidException.class) | |
// 一开始不知道这个错误的异常类是什么先定义 Exception.class | |
public R handleValidException(MethodArgumentNotValidException e) | |
{ // 这里会打印出异常类我们从控制台复制该上面 | |
log.error("数据校验出现问题:{},异常类型:{}", e.getMessage(), e.getClass()); | |
// 实例化 BindingResult 异常对象 | |
BindingResult bindingResult = e.getBindingResult(); | |
// 实例化 map 存储所有该异常的信息 | |
Map<String, String> errorMap = new HashMap<>(); | |
// 遍历异常 | |
bindingResult.getFieldErrors().forEach(r -> { | |
// 添加异常信息 | |
errorMap.put(r.getField(), r.getDefaultMessage()); | |
}); | |
// 将异常信息返回到前端 | |
return R.error(400, "数据校验出现异常").put("data", errorMap); | |
} | |
// 以后可以针对更多的异常来进行不同的处理 | |
//... 此处省略一百行代码 | |
} |