SpringBoot 框架学习记录
SpringBoot 介绍
Spring Boot是由Pivotal团队提供的基于Spring的全新框架,旨在简化Spring应用的初始搭建和开发过程。
Spring Boot是所有基于Spring开发项目的起点。
Spring Boot就是尽可能地简化应用开发的门槛,让应用开发、测试、部署变得更加简单。
SpringBoot 特点
- 遵循“约定优于配置”的原则,只需要很少的配置或使用默认的配置。
- 能够使用内嵌的Tomcat、Jetty服务器,不需要部署war文件。
- 提供定制化的启动器Starters,简化Maven配置,开箱即用。
- 纯Java配置,没有代码生成,也不需要XML配置。
- 提供了生产级的服务监控方案,如安全监控、应用监控、健康检测等。
Spring Boot 与 SSM
- SSM是由三个独立的框架组合而成的,分别是Spring、Spring MVC和MyBatis的缩写。这三个框架经常结合使用,构建Java Web应用程序:
- Spring:用于依赖注入和事务管理。
- Spring MVC:用于Web层,处理HTTP请求。
- MyBatis:是一个持久层框架,用于与数据库交互。
- Spring Boot可以很好地与
Spring MVC
和MyBatis
结合使用,但其目的是简化整个应用程序的配置和部署。
SpringBoot3 环境要求
- Spring Boot 2.7 是最后一个支持 JDK 8 的版本,根据官方公告,Spring Boot 2.7.x 会在2023年11月停止维护。
- 未来能够获得官方免费维护的版本只有Spring Boot 3.0及以上版本,由于Spring Boot 3.x版本要求Java 17作为最低版本,因此需要安装JDK 17或以上版本运行。
Apache Maven
简单介绍
Apache Maven 是一个流行的 Java 项目管理和构建工具,可以对 Java 项目进行自动化的构建和依赖管理。
Maven 的作用
- 项目构建:提供标准的,跨平台的自动化构建项目的方式
- 依赖管理:方便快捷的管理项目依赖的资源(jar包),避免资源间的版本冲突等问题
- 统一开发结构:提供标准的,统一的项目开发结构,如下图所示:
下载 Maven
访问 Apache Maven 的官方下载页:
https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/
下载 “Binary zip archive”,注意不要讲 maven 解压到中文路径下。
Maven 仓库
运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库。
配置国内 Maven 镜像
maven 默认连接的远程仓库位置并不在国内,因此有时候下载速度非常慢,可以配置一个国内站点镜像,可用于加速下载资源。
编辑文件:~/.m2/settings.xml 在 mirrors 标签下添加如下配置:
1 | <mirrors> |
这里以配置华为镜像源为例。
idea 中配置 Maven
默认情况下,idea 集成开发工具,自带 Maven,我们需要设置为我们自己安装的 Maven。
SpringBoot 入门
创建 SpringBoot 项目
Spring Initializr是一个在线工具,用于快速生成一个新的 Spring Boot 项目:https://start.spring.io/
当然国内也可以使用阿里云的镜像:http://start.spring.io 创建。
两者不同的是,SpringBoot 官方创建的 SpringBoot 版本偏新,而 阿里云镜像创建的则偏国内主流。
这里我们采用阿里云的镜像创建。我这里使用 idea 配置阿里云镜像创建 SpringBoot Web 项目。
创建完成的目录如下:
SpringBoot 目录结构
src/main/java/
: 此目录包含项目的主要 Java 源代码src/main/resources/
: 存放项目的资源文件,例如配置文件、国际化属性文件、SQL 脚本等。src/test/
: 此目录用于存放项目的测试代码和测试资源。pom.xml
: Maven的配置文件,定义了项目的依赖、插件和其他设置项目名+Application.java
: 这是 Spring Boot 应用程序的入口点。它通常包含@SpringBootApplication
注释,并包含 main 方法来启动应用程序。
SpringBoot 系统配置
配置文件
Spring Boot 使用配置文件来配置和定制应用程序的行为,例如指定 SpringBoot 程序的端口、文件上传的路径等。
默认配置文件为:application.properties
位于 src/main/resources/
目录中。
当然除了 application.properties
我们还可以将后缀修改为 .yaml
或 yml
SpringBoot 程序会自动识别。
不同的是 properties
配置文件采用的是 key=value 的形式,而 yaml
则采用缩进的方式来表示层级关系。
例如:
实际项目中,yaml
和 yml
格式的配置文件更受程序员们的青睐,后续配置文件我们也已 yml
为主。
自定义配置属性
SpringBoot 有一些内置的配置如:
- 启动和服务器配置 server.port
- 数据库连接spring.datasource
- 日志设置logging.level
同时配置文件还支持一些自定义的属性配置,方便开发时使用。如与外部 API 交互的 URL、超时时间、API 密钥、应用程序的版本号、默认语言等。
Spring Boot 中框架提供了 @Value
和 @ConfigurationProperties
两个关键注解从配置文件中读取并注入属性值。
@Value 的使用案例
@value
注解的作用是读取配置文件中的属性值,并将其注入到字段中。
首先在配置文件中添加如下自定义属性:
1 | # 自定义属性 |
在程序中获取属性值:
1 | package com.qwesec.demo.entity; |
接着,在测试集类中,我们通过依赖注入的方式,获取到 MyAppPropertites
类的实例,并打印出 appName 属性的值。
1 | package com.qwesec.demo; |
可以发现成功输出了配置文件中的属性。
@ConfigurationProperties 的使用案例
@ConfigurationProperties
注解的作用是:将配置文件中的属性映射到 JavaBean 中,从而实现配置文件的统一管理。
1 | package com.qwesec.demo.entity; |
接着在测试类中测试执行:
SpringBoot WEB 应用开发
MVC 架构应用
MVC 的介绍和工作流程
使用Spring Boot开发Web应用程序时,Spring MVC是其背后的主要驱动力,它为Web应用提供了模型-视图-控制器(MVC)的架构和组件。
MVC,即模型(Model)、视图(View)、控制器(Controller),是一种设计模式,用于将应用程序分为三个主要组件。
开发基本的 Web 应用
Spring Boot没有严格的项目结构,但它有一些约定,一个典型的Spring Boot项目的包结构如下:
目录 | 描述 |
---|---|
com.example.myapp |
项目的根包,通常以域名或公司名称的反转来命名 |
controller |
存放处理请求和响应的控制器类 |
service |
包含业务逻辑的服务类 |
repository |
数据访问和持久化层,使用 MyBatis-Plus 等技术时可能命名为 mapper 或 dao |
model |
存放模型类,通常是 POJO(Plain Old Java Object)类,用于表示应用程序中的数据结构 |
config |
存放配置类,用于配置应用程序的一些特殊设置,如数据库连接、安全配置等 |
dto |
数据传输对象(Data Transfer Object),这里存放 DTO 类,用于在层之间传输数据,特别是在 Controller 和 Service 层之间 |
util |
存放工具类,通常包含一些辅助性方法或工具函数 |
构建一个能够响应用户请求的Web服务,只需要经过以下三步:
- 引入spring-boot-starter-web依赖。
- 创建一个带有
@RestController
注解的类。 - 在此类中定义一些带有
@RequestMapping
或其相关注解的方法。
访问:
spring-boot-starter-web
Spring Boot将传统Web开发的mvc、json、tomcat等框架整合,提供了spring-boot-starter-web组件,简化了Web应用配置。
创建SpringBoot项目勾选Spring Web选项后,会自动将spring-boot-starter-web组件加入到项目中。
spring-boot-starter-web启动器主要包括web、webmvc、json、tomcat等基础依赖组件,作用是提供Web开发场景所需的所有底层依赖。
webmvc为Web开发的基础框架,json为JSON数据解析组件,tomcat为自带的容器依赖。
控制器注解
@Controller 的使用案例
@Controller
注解,控制器类中的方法通常返回一个字符串,代表逻辑视图的名称。
这个视图名将被解析为一个实际的视图,例如一个JSP
、Thymeleaf
或 FreeMarker
模板。
现如今一般都是前后端分离的界面,因此很少使用@Controller
注解,而是使用 @RestController
注解。
使用@Controller
注解,页面返回的不再是 index.html
字符串,而是 index.html
页面。
如果要返回 index.html
字符串,则需要使用 @ResponseBody
注解。
因此 @Controller 注解和 @ResponseBody 注解结合使用,才能返回字符串。并且通过只使用 @Controller
我们可以发现,这里很明显是配合模版来使用。
@RestController 的使用案例
@RestController
是 @Controller
和 @ResponseBody
注解的结合体。它标记一个类为控制器,且类中的每个方法默认返回数据而非视图。
访问:
可以看到,这里 SpringBoot 自动将 User 对象转为 JSON 格式返回,这是由于 SpringBoot 内嵌了 Jackson 框架。
在前后端分离的项目中,后端只需要提供 API 接口供前端调用,前端将后端返回的 JSON 数据解析在页面中即可。
请求和响应
请求映射
@RequestMapping
注解是Spring MVC中的核心注解,用于将请求路径映射到特定的控制器方法。
属性 | 描述 | 示例 |
---|---|---|
value/path |
定义 URI 模式,是 @RequestMapping 最常用的属性,类型为 String[] |
@RequestMapping("/books") |
method |
定义 HTTP 请求方法,例如 GET、POST、PUT、DELETE 等,类型为 RequestMethod[] |
@RequestMapping(value="/books", method=RequestMethod.GET) |
params |
定义请求必须满足的参数条件。可以指定参数存在、不存在或具有特定的值,类型为 String[] |
@RequestMapping(value="/books", params="type=novel") 只匹配有 type=novel 参数的请求 |
headers |
定义必须满足的请求头条件,类型为 String[] |
@RequestMapping(value="/books", headers="Referer=http://www.example.com/") 只匹配来自指定 Referer 的请求 |
请求映射中的常见匹配方式
@RequestMapping("/books")
会将所有到/books的请求映射到控制器方法@RequestMapping
注解的method属性允许根据HTTP请求的方法(如GET、POST、PUT、DELETE等)来进一步限定请求的匹配:
1 |
为了简化开发,Spring 还提供了一些快捷注解:
1 | // HTTP的 GET 方法。 |
请求参数绑定
在Web应用中,服务端经常需要获取浏览器传递的数据,例如用于搜索查询、分页、排序和过滤等场景。
在Spring Boot中,有多种注解可用于将请求中传递的数据绑定到处理器方法的参数中,以便获取并处理这些数据。常用的注解包括:
- @PathVariable: 从URI模板中提取值,如从/books/{id}中提取id。
- @RequestParam: 获取查询参数或表单数据。
- @RequestBody: 将请求主体(通常为JSON或XML)绑定到方法参数。
- @RequestHeader: 获取请求头的值。
- @CookieValue: 从cookie中提取值。
@PathVariable 注解
@PathVariable
注解主要从请求路径中获取值,如 /books/{id}
则请求 /books/1
获取的 ID 值就为1。
请求:
可以看到在路径中的 399 就被后端获取到设置为 Book 类的 id 属性值了。
@RequestParam 注解
@RequestParam
注解主要从请求参数中获取值,如 /books?id=1
则请求 /books?id=1
获取的 ID 值就为1。
请求:
如果请求参数与方法中的 id 参数相同,则可以忽略 @RequestParam
注解。
当然也可以接收多个参数同时还能接收通过 POST 请求接收的参数,如果要同时接收多种请求方式传入的参数,就需要使用到 @RequestMapping
注解。
下面看示例:接收多个请求方式传入的多个参数。
1 |
|
这里需要传入 id、title、author、publisher 参数。
GET 请求:
POST 请求:
由于浏览器默认不支持 POST 请求,所以我们需要使用工具,我这里使用的是 ApiPost:
由于我们在控制器中没有接收 price 和 mark 同时并没有为其赋值,所以这两值为默认值 null
。
@RequestBody 注解
相信很多同学看到这里,都觉得接收多个参数时非常麻烦,那么有没有便捷的方法呢?当然有!
那就是使用对象来接收参数。
@RequestBody
注解的作用是将 HTTP 请求体 中的内容绑定到方法参数上,并通过 HttpMessageConverter 转换为所需的 Java 对象
1 |
|
请求:
可以看到,这里可以很轻松的获取到请求体中的内容,并且可以将其转换为所需的 Java 对象并为相关属性自动赋值。
不需要我们手动的进行 book.setId()
等。 但是 Book 类 必须存在 getter
方法!否则会返回 406 状态码 Not Acceptable。
同时 @RequestBody注解 仅支持接收 JSON 格式和XML的格式请求体,如果请求体是 application/x-www-form-urlencoded
格式,则无法正确解析,会返回 400 状态码 Bad Request。
这种方法也是我们开发中特别常用的,因此也就有了 实体类 的存在。
@RequestHeader 注解的使用
@RequestHeader
注解用于获取请求头中的信息
这里以获取请求头中的 User-Agent
和 Host
字段为例:
1 |
|
请求:
@CookieValue 注解的使用
@CookieValue 注解用于获取请求中的 Cookie 值。
这里以获取 Cookie 中的 name 值为例,如果没有 name 值则返回默认值 guest:
1 |
|
如果没有设置,则返回默认值 guest:
JSON 响应
spring-boot-starter-web
依赖自带了 Jackson
库,它可以自动将Java对象序列化为JSON格式。
Jackson 支持将各种Java对象序列化为JSON,包括列表(List)、映射(Map)、集合(Set)、基本数据类型及其包装类等。
当使用@RestController
注解时,Spring Boot会自动使用Jackson库来完成这些对象的序列化。
ResponseEntity
ResponseEntity 内置类用于全面控制HTTP响应,包括状态码、头部信息和响应体内容。
1 | GetMapping("/item/{id}") |
当然常用的还有:
1 | return ResponseEntity.badRequest().body(null); // 返回400 |
一般情况下,我们会自己定义一个通用类,用于构建和返回 HTTP 响应如下:
1 | package com.qwesec.demo.common; |
接着控制器调用响应即可:
1 |
|
内容如下:
如果遇到错误则返回【这里以未认证的返回示例】,由于还没有到数据库查询,因此只是模拟:
1 |
|
国内开发貌似更偏向于这种风格。
构建 RESTful 服务
RESTful 概述
RESTful服务是基于REST(Representational State Transfer,表述性状态转移)架构风格的Web服务。
REST并不是一个标准,它更像一组客户端和服务端交互时的架构理念和设计原则,基于这种架构理念和设计原则的Web API更加简洁,更有层次。
RESTful 特点
在 RESTful 架构中,所有的事物都视为资源,这些资源通过 URI(统一资源标识符)进行标识。例如,在提供书籍和作者信息的服务中,“书”和“作者”都被视为资源,它们分别可以通过类似
/books
和/authors
的 URI 进行访问。资源可以有不同的表示形式,如 JSON、XML 等。客户端请求一个资源时,服务器返回该资源的特定表示形式。客户端和服务器之间的交互完全通过这些表示形式进行。
在 RESTful 架构中,所有的交互都必须是无状态的。这意味着每个请求必须包含所有必要的信息,以便服务器能够理解和处理该请求,而不依赖于之前的请求或存储在服务器上的上下文信息。
表述性状态转移
表述性(Representational):指的是数据的表现形式。例如,一个“书”资源可以用 JSON、XML 或 HTML 格式表示。访问资源时,得到的是资源的一个“表述”,而不是资源本身。
状态(State):指资源的当前状态,如书籍的书名、作者、出版日期等。 REST 是“状态无关”的,意味着每个请求都是独立的,包含了处理请求所需的全部信息。
转移(Transfer):指状态的传递。当客户端请求一个资源时,服务器将资源的“表述”传递给客户端,即状态转移。
RESTful 设计原则
在REST架构中,核心概念是资源,它们通常用名词表示,例如/users而不是/getUser。资源可以是单个实体或对象集合,如用户、订单、产品等。
每个资源基本上都有创建、读取、更新和删除操作。RESTful服务利用HTTP方法来执行对资源的操作,具体包括:
- GET:用于获取资源
- POST:用于创建新的资源
- PUT:用于更新或替换资源
- DELETE:用于删除资源
SB 实现 RESTful 服务
实现 RESTful 所用的注解
- @GetMapping:处理GET请求,获取资源。
- @PostMapping:处理POST请求,新增资源。
- @PutMapping:处理PUT请求,更新资源。
- @DeleteMapping:处理DELETE请求,删除资源。
- @PatchMapping:处理PATCH请求,用于部分更新资源。
用户管理模块 API 示例
HTTP 方法 | 接口地址 | 接口说明 |
---|---|---|
POST | /user | 创建用户 |
GET | /user/id | 根据id获取用户的信息 |
PUT | /user/id | 根据id更新用户的信息 |
DELETE | /user/id | 根据id删除用户的信息 |
具体实现示例代码:
1 |
|
使用 Swagger 生成 API 文档
介绍
swagger 是当下比较流行的实时接口文文档生成工具。接口文档是当前前后端分离项目中必不可少的工具,在前后端开发之前,后端要先出接口文档,前端根据接口文档来进行项目的开发,双方开发结束后在进行联调测试。
swagger 版本
swagger分为swagger2 和swagger3两个常用版本。二者区别不是很大,下面主要以 swagger2 为例进行介绍。
在引入方面,swag2 需要引入两个 jar 包,而 swag3 只需要引入一个即可。
引入依赖
1 | <dependency> |
引入配置
首先需要添加一个注解 @EnableSwagger2
表示开启 swagger 功能,这个注解可以在 SpringBoot 的启动器上,也可以单独定义一个 swagger 的配置类。
这里我们采用第二种方式进行演示,在 config
包下创建 SwaggerConfig
类,如下:
1 | package com.qwesec.demo.config; |
其中 com.qwesec.demo.controller
为控制器的所在包,用于 swagger 扫描。
可能遇到的错误
如果你使用的是 SpringBoot 2.6.x 版本,可能会遇到如下错误,导致项目无法启动:
这是由于两个版本不兼容导致,解决方法就是在 SpringBoot 中添加如下配置:
1 | spring: |
接着重启项目即可:
给 Controller 添加注解
其实到这里我们就可以访问了,路径为:http://ip:port/swagger-ui.html
如下:
这里对所有的 Controller
进行了一个扫描和展示,但是并没有显示每个控制器的作用,因此我们需要再控制器上添加注解对控制器进行描述。
1 |
|
访问:
Swagger 的其他注解
文件上传功能
文件上传原理
浏览器上传文件的过程是基于HTTP协议进行的,并使用特定的请求类型和编码方式来传输文件数据。
前端代码
1 |
|
SB 实现文件上传功能
- 在 Spring Boot 中处理文件上传主要依赖于 Spring Web 的
MultipartFile
接口。 MultipartFile
是一个专门用于处理HTTP请求中上传文件的接口- 当客户端(如浏览器)通过
multipart/form-data
格式的表单提交文件时,Spring MVC可以将这些文件映射为MultipartFile
对象,以便在服务器端进行处理。
MultipartFile 接口
以下是 MultipartFile
接口的一些主要方法:
方法名 | 描述 |
---|---|
byte[] getBytes() |
返回文件的内容为字节数组 |
String getContentType() |
返回文件的 MIME 类型,如 image/jpeg 或 text/plain |
InputStream getInputStream() |
返回一个 InputStream ,允许你读取文件的内容 |
String getName() |
返回参数名称,例如:当表单中的 input 元素的 name 属性为“file”时,这个方法会返回“file” |
String getOriginalFilename() |
返回客户端在文件系统中的原始文件名 |
boolean isEmpty() |
返回文件是否为空 |
void transferTo(File dest) |
将上传的文件保存到目标文件或目录中 |
在控制器中,可以使用 @RequestParam
注解与 MultipartFile
类型的参数来接收上传的文件。
在这个示例中,假设有一个包含名为 file
的 input
元素的表单用于文件上传。当用户提交表单时,可以在服务器端使用 MultipartFile
对象来访问和处理这个文件。
1 |
|
上传配置
在 application.properties
中,可以配置文件上传的相关属性,例如文件大小限制和存储位置。
spring.servlet.multipart.max-file-size
定义了单个上传文件的最大大小。如果文件超过此限制,将抛出异常。spring.servlet.multipart.max-request-size
定义了整个multipart
请求的最大大小,包括所有文件和表单数据。
例如:
1 | spring: |
为了统一管理文件的上传路径,我们可以在配置文件中自定义一个属性,假设为 upload.path
:
1 | spring: |
当然我们还可以将文件上传到 oss 存储桶中进行存储。
后端代码实现
首先创建一个 FileUploadController
类,用于处理文件上传请求:
1 | package com.qwesec.demo.controller; |
这里用到了响应封装类 R
,用于返回 JSON 格式的数据。
以上代码将我们上传的文件保存在项目路径下的 uploads
目录下,并返回一个包含文件相对路径的 JSON 对象,但是项目路径下的 uploads
前端是无法访问到的:
可以看到这里返回404,前端无法访问到上传的文件,因此我们还需要添加 uploads
为静态目录。
静态资源目录
在 Spring Boot 应用中,如果不进行任何自定义配置,框架默认会搜索以下类路径下的目录并将它们作为静态资源目录:
- /META-INF/resources/
- /resources/
- /static/
- /public/
将静态文件(如HTML、CSS、JS或图片)放置在这些目录下,Spring Boot 会自动提供这些文件的访问。
通过在 application.properties
文件中设置 spring.resources.static-locations 属性来指定静态资源的路径,例如:
1 | spring: |
如果在这些目录下有一个名为 image.jpg 的文件,可以通过如下 URL 访问:
1 | http://yourserver:port/image.jpg |
这里就需要在 resources 目录下创建 custom-path 文件夹。
配置类设置静态目录
当然我们还可以创建配置类的形式指定静态资源路径,这里我们通过配置类的形式设置静态目录:
1 | package com.qwesec.demo.config; |
再次上传文件并访问:
文件下载功能
1 | package com.qwesec.demo.controller; |
访问:http://localhost:8090/api/files/download?filename=1734335322733.jpg 即可完成下载。
其中filename为要下载的文件名称,只允许下载 uploads
目录下的文件,防止使用 ..
进行目录穿越,造成任意文件下载(读取)漏洞。
数据验证与异常处理
数据验证
介绍
- Spring Boot 通过集成 Hibernate Validator 和使用 Java 的 Bean Validation API,为开发者提供了一套强大、灵活且易于使用的数据验证机制。
- 要在Spring Boot应用程序中使用数据验证,首先需要添加相关的依赖,找pom.xml中加入以下依赖:
引入依赖
1 | <dependency> |
验证相关的注解
注解名称 | 描述 |
---|---|
@Size(min=, max=) |
确保字段的值的大小/长度在指定的范围内。 |
@Min(value=) |
确保字段的值大于或等于给定的最小值,仅对数字类型有效。 |
@Max(value=) |
确保字段的值小于或等于给定的最大值,仅对数字类型有效。 |
@NotBlank |
确保某个字符串属性在验证时不为空,并且其去除首尾空白后的长度至少为1。 |
@Email |
确保字段值是电子邮件地址。 |
@Pattern(regexp=) |
确保字段的值与给定的正则表达式匹配。 |
使用案例
这里我们可以创建一个 dao 包并创建 UserDTO 的类,当然我们也可以在 entity
创建 User
类:
1 | public class User { |
以上验证了 用户名和密码 以及 gender 和 手机号和邮箱。
其中 username
和 password
的注解 @Size
可以设置最小和最大长度,gender
参数使用了 @NotBlank
注解,确保其不为空,并且去除首尾空白符后长度至少为1。phone
参数使用了 @Pattern
注解,确保其符合指定的正则表达式,即满足11位手机号码的格式。最后使用自带的 @Email
注解 和 NotBlank
来验证邮箱的格式以及确保该值不为空且满足邮箱的格式。
创建控制器进行测试:
1 |
|
这里需要使用 @Valid
注解来启用 User 类中的验证功能。
接着来访问测试,不传入邮箱参数:
可以看到返回 400 状态码,来到程序看输出信息:
由于这里抛出的是 MethodArgumentNotValidException
异常,是在进入你的业务方法之前抛出的,所以即使你在业务方法内部使用 try-catch
,也无法捕获它
但是我们需要将错误信息返回给前端。于是就需要使用全局异常器来处理异常,将具体的错误信息返回给前端,前端反馈给用户。
全局异常处理
介绍
全局异常处理允许在一个集中的位置处理所有的异常,确保整体的用户体验和应用的响应行为始终如一。
在 Spring Boot 中,全局异常处理通常是通过使用
@RestControllerAdvice
和@ExceptionHandler
注解来实现的。@RestControllerAdvice
是@ControllerAdvice
的特殊变种,它默认将结果作为JSON 返回,非常适合 RESTful 服务。@RestControllerAdvice
允许为多个@RestController
类定义全局、跨切面的行为,它们不直接处理HTTP请求。当需要定义一些对多个控制器都适用的行为时,例如,当多个控制器都需要相同的异常处理逻辑时,
@RestControllerAdvice
是最合适的选择。@ExceptionHandler
用于处理控制器中的特定异常。这个注解提供了一种优雅的方式来集中处理特定的异常类型,而不是在每个控制器方法中使用try-catch
块。
使用案例
1 |
|
使用我们使用了 @RestControllerAdvice
注解,表示这个类是一个全局异常处理器。然后我们定义了一个 @ExceptionHandler
注解,用于处理 MethodArgumentNotValidException
类型的异常。
接着获取错误的信息并调用 R.error
方法返回给前端。R
类是我们在前面自己封装的一个响应类。
最后在我们要使用全局异常处理器的 Controller
上添加 @Validated
注解,这样在参数校验时,就会使用全局异常处理器来处理异常。
1 |
|
测试邮箱参数为空:
测试密码参数小于8位:
可以看到将错误(验证)信息返回给前端了。最后经过前端处理响应给用户。
最后通过测试:
当然,这里不仅可以返回验证信息,全局异常处理还可以返回其他错误信息,我们之前是通过 try-catch 来捕获异常,返回给前端的,现在我们可以通过全局异常处理,统一返回给前端。
这里我们故意写一个错误:
接着在全局处理器中定义捕获该异常并将错误信息以 json 格式返回给前端:
当然也可以直接返回 被除数不能为0
字样,这样就可以很优雅的处理这些异常了哈哈。
拦截器
前言
拦截器(Interceptor)是一种设计模式,用于在某个操作或请求的前后插入特定的行为或处理逻辑。
在Web开发中,拦截器通常用于在处理HTTP请求的前后执行某些操作:
- 身份验证和授权:在请求到达目标处理器之前,进行用户身份验证和权限检查,例如对接口进行校验,防止未授权漏洞的产生。
- 日志记录:记录请求的详细信息,如来源IP、请求的URL、请求方法以及响应时间和执行时长。
- 数据处理与监控: 预加载请求所需的数据,记录请求处理时间以进行性能监控,限制
HandlerInterceptor 接口
preHandle()
:请求处理之前调用。如果返回true,请求继续进行;返回false,请求将中断。postHandle()
:在请求被处理之后,但在视图被渲染之前调用。afterCompletion()
:请求处理完毕后调用,这包括视图的渲染。通常用于资源清理操作。
创建拦截器
定义一个类 CustomInterCeptor
实现 HandlerInterceptor
接口,这里为了规范,我们创建 Interceptor
包,在该包下创建相应的拦截器:
1 | public class CustomInterceptor implements HandlerInterceptor { |
拦截器配置
接着我们在 WebConfig
类配置拦截器,指定拦截哪些路径和排除那些路径:
1 |
|
WebConfig
类实现了 WebMvcConfigurer
接口,并重写了 addInterceptors
方法,指定使用 CustomInterceptor
作为拦截器,要拦截的路径为 "/api/v2/user/
路径下的所有接口,但是排除登陆和注册接口,也就是说除了登陆和注册,其他都会被拦截器所拦截,因此该功能一般用于鉴权。
测试拦截器
在拦截器配置中我们拦截了 /api/v2/user/
路径下的所有接口,但是排除了 login
和 register
接口,因此我们编写 Controller
进行测试:
1 |
|
运行项目并访问:
可以发现请求会先进入拦截器中的 preHandle
方法当中进行判断是否往下执行。如果往下执行,则会执行 getUserById
方法中的代码。
最后再执行后逻辑处理和请求完成后的逻辑处理。
我们尝试访问 login
和 register
接口,可以发现拦截器不会拦截,因为这两个接口在排除列表中。
可以发现,并未被拦截器所拦截。
数据库集成和持久化
Mybatis 的基础使用
数据持久化和 ORM
在现代应用开发中,数据持久化是一个关键概念,它涉及将数据保存在持久存储如(数据库中),以确保即时在程序关闭或崩溃下,数据仍然被保留。直接与数据库及交互可能操作复杂且容易出错,尤其在面向对象编程中, ORM 显得尤为重要。
对象关系映射(Object-Relational Mapping, ORM)允许开发者以操作对象的方式与数据库交互,简化了数据库操作过程。
MyBatis 是一种半自动 ORM 框架。它允许开发者直接编写 SQL,同时提供了结果映射到对象的能力。与完全自动的 ORM 工具相比,MyBatis 提供更多的灵活性和控制力。
Mybatis 的核心特性如下:
- 灵活的 SQL 查询,允许按照开发者的的风格编写 SQL,充分利用数据库功能。
- 强大的映射功能:支持多种复杂映射,如一对一,一对多等。
- 双向配置:提供直接和 XML 两种配置方式
- 动态SQL:支持根据运行时条件改变的 SQL 查询。
- 内置连接池和事务管理,简化连接和事务管理,也支持集成第三方工具
SprintBoot 集成 Mybatis
前言
Mybatis 官方为简化 Mybatis 在 SpringBoot 项目中的集成,提供了 mybatis-spring-boot-starter
,通过这个 starter 开发者可以轻松的将 mybatis 集成到 SpringBoot 应用当中。
提供如下便捷:
- 自动配置:SpringBoot Starter 的核心优势在于其自动配置的能力,它会自动配置 Mybatis 的关键组件,如
SqlSessionFactory
和SqlSessionTemplate
。 - 无需 XML:尽管 Mybatis 支持 XML 配置,但通过使用
Starter
开发者可以通过纯 Java 配置和注解使用 Mybatis,从而减少对 XML 的依赖。 - 集成 Spring 事务:Starter 与 Spring 事务无缝集成,允许使用
Spring
的@Transactional
注解来管理事务。 - 灵活的属性配置:通过 SpringBoot 的配置文件(application.properties) 可以轻松的配置 Mybatis 的各种属性。
引入依赖
1 | <dependency> |
我这里使用的 SpringBoot 版本为 2.6.13,故而这里需要选择 2.3.2
,高版本的可以选择 3.0.x
, 不然会因为依赖不兼容问题,导致运行出错。
配置数据源
1 | spring: |
spring.datasource.driver-class-name
指定数据库驱动类,在 mysql8 驱动中,则需要添加
cj`。spring.datasource.url
定义数据库服务器的地址和端口以及要操作的数据控,该案例中的数据库名为springbootdemo
此处的URL包含了数据库地址(localhost:3306
),数据库名称(springbootdemo
)以及一些连接参数如禁用SSL,设置服务器时区为 UTC,最后设置字符集编码为 UTF-8spring.datasource.username
指定连接数据库所使用的用户名spring.datasource.password
指定连接数据库所使用的密码
测试所需的数据库和表创建
我这里创建的数据库名称为:springbootdemo
,各位同学根据需求创建:
1 | CREATE DATABASE springbootdemo; |
插入测试数据:
1 | INSERT INTO `orders` (`id`, `user_id`, `order_date`, `amount`) VALUES (1, 1, '2024-12-18', 2000.00); |
定义数据库表映射的实体类
定义对数据库中 users
表和 orders
表的映射对象:
1 | public class User { |
1 | public class Order { |
这里定义的实体类属性默认要与表的字段名一致,但是表中字段可能存在下划线命名法,如 registered_date
而 Java 则常用的是驼峰命名法,Mybatis 提供了自动映射这两种命名格式的功能,要启动这一特性,需要在 application.yaml
添加如下配置:
1 | mybatis: |
由于 users 表中包含日期类型的字段,因此在序列化和反序列化含有日期和时间的对象时,时区设置可能会导致问题出现, Spring boot 使用的 Jackson 默认采用 GMT
(格林尼治标准时间),作为时区,为避免时区显示错误,建议在 application.yaml
配置文件中添加如下配置:
1 | spring: |
Mybatis 映射介绍
Mybatis 映射是其核心功能之一,主要负责描述如何将 SQL 查询结果转为 Java 对象,以及如何从 Java 对象转换到 SQL 查询参数。
映射定义了数据库与 Java 对象之间的数据转换规则。
映射的主要组成部分如下:
- SQL 语句:要执行的具体 SQL 操作,如查询、插入、更新或删除。
- 输入映射:描述如何从 Java 对象提取值,并作为 SQL 语句的参数。
- 输出映射:描述如何从 SQL 查询结果中提取值,并填充到 Java 对象的属性中
映射可以通过以下两个方式定义:
- XML 映射:在 XML 文件中定义 SQL 语句、输入映射和输出映射。适用于复杂的查询和映射场景。
- 注解映射:在 Java 接口方法上使用 MyBatis 注解(如 @Select、@Insert)定义映射。适用于简单场景。
Mapper 接口是映射的 Java 表现形式,提供了类型安全的方式来使用映射。每个 Mapper 接口方法对应一个 SQL 操作。
当使用 MyBatis 时,主要分为以下三步:
- 定义 Mapper 接口:创建一个接口,其中包含与 SQL 操作相
- 对应的方法。
- 定义映射方法:在 Mapper 接口中的方法中,通过注解或与外部 XML 文件关联来定义 SQL 语句。
- 调用映射方法:在应用程序中调用 Mapper 接口的方法,MyBatis 会找到相应的映射并执行 SQL 语句,返回处理后的结果。
Mybatis 映射
Mapper 接口一般都被放在 mapper
包下,接口名称一般由 数据表名+Mapper
进行命名,这里以操作 users
表为例:
1 |
|
@Mapper
注解表示接口为 Mybatis Mapper 接口,允许 Mybatis 生成代理对象来实现该接口,可以在应用程序中不编写任何具体实现的情况下,直接调用这个接口的方法来与数据库进行交互。这个注解还指示 SpringBoot 容器扫描并管理该接口作为一个Bean
,使得它可以在其他组件(如Services、Controller)中通过@Autowired
注解成功注入。@Select
注解用于查询数据,该注解的值是一个 SQL 语句,其中${id}
是一个参数占位符,用于将方法参数的值替换进去。
如果每个 Mapper 都在 mapper
包下,那么我们就可以在 SpringBoot 的启动器上添加 @MapperScan
如下代码所示:
1 |
|
通过这种方式 SpringBoot 会自动扫描 com.qwesec.demo.mapper
包下的所有接口,都会被 Mybatis 自动识别并注册为 Mapper 接口,这样就不用单独为一个 Mapper 添加 @Mapper
注解了。
一个简单的查询示例
1 |
|
测试:
注解方式操作数据库
- 在 MyBatis 中,注解方式提供了一种更加简洁的方法来执行数据库的 CRUD(创建、读取、更新、删除)操作:
@Select
: 执行SELECT查询。@Insert
: 执行INSERT操作。@Update
: 执行UPDATE操作。@Delete
: 执行DELETE操作。
编写 Mapper 文件,添加增删改查的注解和方法:
1 |
|
接着来到控制器中使用:
1 |
|
根据ID查询用户测试:
查询所有用户测试:
添加用户测试:
更新用户测试:
删除用户测试:
持续更新…
参考文献
- 本文主要参考刘伟老师写的《快速搞定Spring Boot+Vue全栈开发》
- SpringBoot 集成 swagger :https://juejin.cn/post/7078109157656231944