[前端学java10-SpringBoot实战] bean赋值转换 + validation校验 + @RestControllerAdvice全局异常处理

导航

[react] Hooks

[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式

[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[数据结构和算法01] 二分查找和排序

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例

[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson
[前端学java08-SpringBoot实战总结1-7] 阶段性总结
[前端学java09-SpringBoot实战] 多模块配置 + Mybatis-plus + 单多模块打包部署
[前端学java010-SpringBoot实战] bean赋值转换 + 参数校验 + 全局异常处理

(一) 前置知识

(1) 一些单词

scalar 纯量
quote 引用 
// single quotes 单引号
// double quotes 双引号
palette 调色板

implicit 隐式,含蓄
hibernate 冬眠 休眠
Advice 建议 通知

persistent 持久的,执着的
profile 概述 简介 环境 // spring.profile
assert 断言
future 未来 将来 // feature 特征

digits 数字 位数
integer 整数
fraction 小数

advice 建议 通知 // @ControllerAdvice
exception 异常
abstract 抽象的

public 任何对象都可以访问
private 只能被自己的方法访问,子类和其他任何类都不能访问
protected 只能被自己和子类访问,其他类型的类不能访问
final 不能被继承
static 可以通过类自身和实例来调用
abstract 抽象类,不自己实现方法,子类需要实现方法

(2) @ConfigurationProperties + @Component 实现配置绑定

  • 注意区分 @Component 和 @Configuration 的区别
bean
-------
// @Component + @ConfigurationProperties 两个组件配合,用来实现 ( 配置绑定 ),绑定application.properties或者application.yml文件中的变量
// @Component 把组件注册到容器中,即向IOC中添加组件 => 必须在容器中的组件,才能拥有spring提供的强大功能,即容器中的组件才能使用 @ConfigurationProperties
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Component // 向IOC中添加组件
@ConfigurationProperties(prefix = "myapp") // prefix 即 application.yml中的前缀
public class TestConfigurationPropertiesAndComponentBean {
    String author;
    String email;
}


application.yml
-------
myapp: # 通过 @ConfigurationProperties(prefix = "myapp") 指定
  author: woow_wu7
  email: woow.wu7@gmail.com

(3) 复习拦截器 - HandlerInterceptor + WebMvcConfigurer

(1)
HandlerInterceptor
  - preHandle => controller目标方法执行前
  - postHandle => controller目标方法执行后
  - afterCompletion => 页面渲染完后
  
(2)
具体流程
1. 在 ( interceptor ) 文件夹中添加拦截器,并实现 ( HandlerInterceptor ) 接口,并在 ( preHandle ) 等中处理逻辑
2. 在 ( config ) 文件夹中通过实现 ( WebMvcConfigurer ) 接口,然后添加拦截规则

(4) 复习 swagger

1. 安装
<!-- Swagger -->
<!-- 自动生成 ( 接口文档 ) 及 ( 自测工具 ) -->
<!-- Swagger2 3.0 只需要 ( springfox-boot-starter ) 就可以了 -->
<!-- Swagger2 2.x 则需要 ( springfox-swagger2 ) 和 ( springfox-swagger-ui )-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>


2. 编写配置类
src/main/java/com.example.demo/config/Swagger2Config.java
-------
@Configuration // 标注当前类是一个启动类,即项目启动时就要去加载了
@EnableSwagger2 // 开启 Swagger2 的配置
public class Swagger2Config {
    @Bean // 把该组件添加到IOC容器中
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                // Swagger2进行包扫描,扫描 controller,这里填写 controller 的文件夹全路径
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                // 标题
                .title("react-admin-java-api 使用Swagger2构建RESTful APIs")
                // 描述
                .description("react-admin-java-api Swagger2的接口文档")
                // 作者信息
                .contact(new Contact("woow_wu7", "https://github.com/woow-wu7/7-react-admin-java", "woow_wu7@gmail.com"))
                // 服务网址
                .termsOfServiceUrl("http://120.53.220.141:81/admin-home")
                .version("1.0")
                .build();
    }
}


3. 
访问 http://localhost:7000/swagger-ui/index.html
即是: 项目启动地址/swagger-ui/index.html
如果是远程部署后访问地址:服务器地址/swagger-ui/index.html

(5) 复习一些前端的知识

(1) trim
- 字符串的 ( trim ) 方法用来去除 ( 字符串 ) 两端的空格,返回一个 ( 新的字符串 ),不改变原字符串

(2) git rm -r --cached 文件夹或者文件
- 比如:git rm -r --cached dist
- 表示:把 ( 当前dist ) 的 ( 跟踪记录删除 ),但是 ( 工作区的dist文件夹还是存在 )
- git rm 
  - 同时从 ( 工作区和索引 ) 中删除文件,即本地文件也被删除了
- git rm --cached
  - 从 ( 索引中删除 ) 文件,本地文件还是存在,但该文件不被版本所控制了
- git rm -r
  - 上面的 -r 是 ( recursive ) 递归的意思
  - 总的表示 递归的删除

(6) 多模块配置注意的问题

  • 关于mybatis的xml方式配置
    • mapper模块
      • resources/maybaits/mybatis-config.xmlmybatis/xxx.xml 是在 mapper 模块配置的
    • controller模块
      • 但是关于mybatis的配置文件指定,是在 controller 模块中的 application.yml 文件中配置的,因为在 controller模块中才具有启动类Application

(7) public private protected static final abstract

  • public
    • 公共访问控制符
    • 将一个类声明为 ( 公共类或方法或属性 ),可以 ( 被任何对象访问 )
    • 一个程序的主要类一定是公共类
  • protected
    • 保护访问控制符
    • 只允许自己和子类访问,其他类型的类不能访问
  • private
    • 私有访问控制符
    • 只允许自己类的方法访问,其他类(包括子类)的方法都不能访问
  • abstract
    • 将一个类声明为 ( 抽象类 ),没有实现的方法,需要子类实现
    • final和abstract不能同时出现
  • final
    • 将一个类声明为 ( 非继承类 ),表示该类 ( 不能被其他类继承 )
    • final和abstract不能同时出现
  • static
    • 修饰的方法和变量,既可以通过 ( 类来调用 ),也可以通过 ( 实例对象调用 )
    • 如果没有通过static修饰,则该类必须先实例化,然后再通过实例对象调用

(8) java异常分类 - Throwable + Error + Exception

image.png

(9) java中的枚举enum类型

  • java中枚举是一种特殊的类,一般用来表示 ( 一组常量 )
// 枚举 - enum
// CustomExceptionType - 自定义 异常枚举类型

public enum CustomExceptionType {
    CLIENT_ERROR(400, "客户端错误"),
    SERVICE_ERROR(500, "服务端错误"),
    OTHER_ERROR(999, "未知错误");

    private String desc; // 异常描述
    private int code; // 异常状态码

    CustomExceptionType(int code, String desc) { // 有参构造器
        this.code = code;
        this.desc = desc;
    }

    public int getCode() { // getter
        return code;
    }

    public String getDesc() { // setter
        return desc;
    }
}

(二) java-bean 赋值转换

image.png
  • VO ( View Object 和前端view层数据结构相对应 )
  • BO/DTO ( Business object / Data Transfer Object 通常是多个PO的组合体 )
  • POPersistent Object 持久对象,该类的成员与数据库字段一一对应 )
VO(view object)
    ---------------------------------- controller
BO(business object)
    ---------------------------------- service
PO(persistent object)
    ---------------------------------- mapper

(三) 参数验证 spring-boot-starter-validation

  • spring-boot-starter-validation
image.png

(1) 安装验证相关依赖

<!--校验组件-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--web组件-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

(2) 添加验证规则

  • 一般用于POST,PUT请求
  • 1.在bean实体中添加规则 比如:@NotNull
  • 2.在controller的参数中添加 比如:@Validated 或者 @Valid 作用于bean参数
bean
-------
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Component
@ApiModel(description = "返回响应的数据") // swagger
public class MusicBean {
    @ApiModelProperty(value = "歌曲名") // swagger
    String name;

    @NotBlank(message = "album不能为空") // spring-boot-starter-validation => @NotBlank只能用于字符串,trim后长度必须大于0
    String album;

    @NotNull(message = "singer不能为空") // spring-boot-starter-validation => @NotNull不能为为null,但可以是空字符串
    String singer;

    Timestamp startTime;
    Timestamp endTime;
}



controller
-------
// 增
@PostMapping("/music")
public Integer addMusic(
        @Validated // spring-boot-starter-validation 验证插件,然后在music实体中添加具体的验证规则
        @RequestBody MusicBean music
) {
    return musicService.addMusic(music);
}

(3) 上面 2 中存在的问题

  • 如果上面的2中的 singer 为null,后端就会给前端抛出错误,但是写的具体的错误信息不会返回给前端
  • 所以我们需要在controller的方法中添加参数,类型是 BindingResult
// 增
@PostMapping("/music")
public Object addMusic(
        // @Validated // spring-boot-starter-validation 验证插件,然后在music实体中添加具体的验证规则
        @Valid
        @RequestBody MusicBean music,
        BindingResult br
) {
    if (br.hasErrors()) {
        log.info(br.getFieldError().getDefaultMessage());
        return br.getFieldError().getDefaultMessage(); // 将错误返回给前端
    }
    return musicService.addMusic(music);
}
image.png

(4) 上面 3 中存在的问题

  • 错误是返回了,但是 状态码还是200 则需要使用 HttpServletResponse
// 增
@PostMapping("/music")
public Object addMusic(
        // @Validated // spring-boot-starter-validation 验证插件,然后在music实体中添加具体的验证规则
        @Valid
        @RequestBody MusicBean music,
        BindingResult br, // spring-boot-starter-validation
        HttpServletResponse resp // spring-boot-starter-validation
) {
    if (br.hasErrors()) {
        resp.setStatus(412); // 412 一个或者多个请求头字段在当前请求中报错
        log.info(br.getFieldError().getDefaultMessage());
        return br.getFieldError().getDefaultMessage();
    }
    return musicService.addMusic(music);
}

(5) @NotBlank @NotEmpty @NotNull @NoneNull 的区别

  • @NotEmpty 用于类上 不能为null,并且长度必须大于0
  • @NotNull 用于类的属性上 不能为null,但是可以是空字符串
  • @Blank 用于类的属性上,并且只用于String类型 不能为null,而且调用trim()后,长度必须大于0
  • @NonNull 用于方法或构造函数的参数上使用,生成一个空值检查语句

(6) @Valid 和 @Validated 的区别


(四) 全局异常处理

  • @RestControllerAdvice
  • @ExceptionHandler
  • RuntimeException

(4.1)具体流程

  • 新建exception的package,然后新建三个文件
    • CustomExceptionBean 错误信息收集
    • ApiResponseBean 返回给前端的最终数据结构
    • WebExceptionHandler

(4.1.1) CustomExceptionBean

CustomExceptionBean
-------

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CustomExceptionBean extends RuntimeException{
    public int code;
    public String message;
}

(4.1.2) ApiResponseBean

ApiResponseBean
-------

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class AjaxResponseBean {
    public boolean isOk;
    public int code;
    public String message;
    public Object data;

    // 成功 - 无数据
    // 请求成功的响应,返回值不带查询数据,( 用于添加,删除,修改 )
    public AjaxResponseBean success() {
        AjaxResponseBean ajaxResponseBean = new AjaxResponseBean();
        AjaxResponseBean.builder()
                .isOk(true)
                .code(200)
                .message("请求成功")
                .build();
        return ajaxResponseBean;
    }


    // 成功 - 带数据
    // 请求成功的响应,返回值带查询数据,( 用于查询 )
    public static AjaxResponseBean success(Object obj) {
        AjaxResponseBean ajaxResponse = new AjaxResponseBean();
        AjaxResponseBean.builder()
                .isOk(true)
                .code(200)
                .message("请求成功")
                .data(obj)
                .build();
        return ajaxResponse;
    }

    // 失败
    public static AjaxResponseBean error(CustomExceptionBean customExceptionBean) {
        AjaxResponseBean ajaxResponse = new AjaxResponseBean();
        AjaxResponseBean.builder()
                .isOk(false)
                .code(customExceptionBean.getCode())
                .message(customExceptionBean.getMessage())
                .build();
        return ajaxResponse;
    }
}

(4.1.3) WebExceptionHandler

WebExceptionHandler
-------

@RestControllerAdvice
public class WebExceptionHandler {

    @ExceptionHandler
    public AjaxResponseBean customException(
            CustomExceptionBean customExceptionBean
    ) {
        customExceptionBean.setCode(400);
        return AjaxResponseBean.error(customExceptionBean);
    }
}
image.png
image.png

资料

mysql常见crud https://www.jianshu.com/p/49fcde3e4559
swagger常用注解 https://blog.csdn.net/jiangyu1013/article/details/83107255
@ControllerAdvice https://fookwood.com/spring-boot-tutorial-11-ca
public private protected final static abstract 的区别

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容