# SSM 整合

# SSM 整合流程

1. 创建工程

2.SSM 整合

  • Spring

    • SpringConfig
  • MyBatis

    • MybatisConfig
    • JdbcConfig
    • jdbc.properties
  • SpringMvc

    • ServletConfig
    • SpringMvcConfig

3. 功能模块

  • 表与实体类

  • dao (接口 + 自动代理)

  • service (接口 + 实现类)

    • 业务层接口测试 (整合 JUnit)
  • controller

    • 表现层接口测试 (PostMan)
  • 事务处理

查看代码

# 表现层数据封装

  • 前端接收数据格式

  • 增删改

image_2023-03-04-22-30-27

  • 查单条

image_2023-03-04-22-30-43

  • 查全部

image_2023-03-04-22-30-53

  • 。。。

  • 每种操作对应不同的格式,以后说不定还有更多的格式于是就想了一个方案

  • 创建结构模型类,封装数据到 data 属性中

  • 增删改

image_2023-03-04-22-32-50

  • 查单条

image_2023-03-04-22-33-10

  • 查全部

image_2023-03-04-22-33-23

  • 但是不知道执行的操作是什么

image_2023-03-04-22-36-37

  • 于是乎:封装操作结果到 code 属性中

  • 增删改

image_2023-03-04-22-38-03

  • 查单条

image_2023-03-04-22-38-36

  • 查全部

image_2023-03-04-22-38-50

  • 但是查询出来的结果怎么看呢只知道查询的 code 编号是 20041

  • 于是乎:封装特殊信息到 message (msg) 属性中

  • 20041 表示成功,20040 表示失败,用数据的最后一位的 0 和 1 来表示

  • 查询单条

image_2023-03-04-22-43-25

  • 设置统一数据返回结果类
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

image_2023-03-04-23-17-21

# 异常处理器

  • 程序开发过程中不可避免的会遇到异常现象

image_2023-03-05-13-15-50

  • 出现异常现象的常见位置与常见诱因如下:

    • 框架内部抛出的异常:因使用不合规导致
    • 数据层抛出的异常:因外部服务器故障导致 (例如:服务器访问超时)
    • 业务层抛出的异常:因业务逻辑书写错误导致 (例如:遍历业务书写操作,导致索引异常等)
    • 表现层抛出的异常:因数据手机,校验等规则导致 (例如:不匹配的数据类型间导致异常)
    • 工具类抛出的异常:因工具类书写不严谨不够健壮导致 (例如:必要释放的连接长期未释放等)
  • 各个层级均出现异常,异常处理代码书写在哪一层

    • <font style="color:red"> 所有的异常均抛出到表现层进行处理 </font>
  1. 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不大,如何解决?
    • 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

  • 类型:方法注解

  • 位置:专用于异常处理的控制器方法上方

  • 作用:设置指定异常的处理方案,功能等同与控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

  • 说明:

    • 此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

image_2023-03-05-13-51-14

  • 异常处理器处理效果比对

Before

image_2023-03-05-13-52-57

After

image_2023-03-05-13-51-55

# 项目异常处理方案

  • 项目异常分类

image_2023-03-05-19-18-18

image_2023-03-05-19-18-40

  • 不规范的用户行为操作产生的异常

image_2023-03-05-19-20-33

  • 规范的用户行为产生的异常

  • 或者用户直接在 age 字段中输入的 haha 那么程序直接可能就异常了

  • 这是用户行为,还有非用户行为比如说数据库垮掉了,这种异常可预计但不可避免

image_2023-03-05-19-23-27

  • 编程人员未预期到的异常

  • 项目异常分类

    • 业务异常 (BusinessException)
      • 规范的用户行为产生的异常
      • 不规范的用户行为操作产生的异常
    • 系统异常 (SystemException)
      • 项目运行过程中可预计且无法避免的异常
    • 其它异常 (Exception)
      • 编程人员未预期到的异常
  • 项目异常处理方案

    • 业务异常 (BusinessException)
      • 发送对应消息传递给用户,提醒规范操作
    • 系统异常 (SystemException)
      • 发送固定消息传递给用户,安抚用户
      • 发送特定消息给运维人员,提醒维护
      • 记录日志
    • 其它异常 (Exception)
      • 发送固定消息传递给用户,安抚用户
      • 发送特定消息给编程人员,提醒维护 (纳入预期范围内)
      • 记录日志

# 步骤

  1. 自定义项目系统级异常
@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;
    }
}
  1. 自定义项目业务级异常
@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;
    }
}
  1. 自定义异常编码 (持续补充)
@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;
}
  1. 触发自定义异常
@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);
    }
}
  1. 拦截并处理异常
@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());
    }
}
  1. 异常处理器效果对比

image_2023-03-05-20-26-36

image_2023-03-05-20-26-49

# 另一种方式 (推荐,结合上述)

定义一个异常处理类

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);
    }
   // 以后可以针对更多的异常来进行不同的处理
   //... 此处省略一百行代码
}