Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的项目。它简化了 Spring 应用程序的搭建和开发过程,提供了一系列默认配置,减少了繁琐的手动配置,使得开发者能够更专注于业务逻辑的实现。
SpringBoot程序优点
- 自动配置
- 起步依赖(简化依赖配置)
- 辅助功能(内置服务器,.....)
SpringBoot在创建项目时,采用jar的打包方式
SpringBoot的引导类是项目的入口,运行main方法就可以启动项目
启动方式:引导类
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
注解标识了主类,并通过 SpringApplication.run
启动了 Spring Boot 应用程序。Spring Boot 会自动配置并启动嵌入式服务器,并根据依赖关系自动配置其他必要的组件。
Spring Boot原理
起步依赖
starter
- SpringBoot中常见项目名称,定义了当前项目使用的所有项目坐标,以达到减少依赖配置的目的
Spring Boot Starter Parent: Spring Boot 提供了一个父项目(spring-boot-starter-parent
),开发者可以继承这个父项目,从而获得一系列的默认配置和依赖管理。这样就能够确保应用的构建和依赖管理与 Spring Boot 版本保持一致。spring-boot-starter-parent (2.5.0) 与 spring-boot-starter-parent (2.4.6)共计57处坐标版本不同
实际开发
- 使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供
- 如发生坐标错误,再指定version (要小心版本冲突)
原理是Maven的依赖传递
自动配置(*)
SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
@SpringBootApplication
是 Spring Boot 的核心注解之一,用于标注一个主程序类,通常是项目的入口类。这个注解实际上是一个组合注解,包括了以下三个注解:
-
@SpringBootConfiguration
: 表示这是一个 Spring Boot 配置类,其中封装了@Configuration,
所以在启动类中可以声明第三方bean对象
-
@EnableAutoConfiguration
: 开启自动配置功能,Spring Boot 实现自动化配置的核心注解。其中封装了@Import({AutoConfigurationImportSelector.class}),AutoConfigurationImportSelector是一个实现类,实现了DeferredImportSelector接口,该接口的父接口ImportSelector中的String[] selectImports(AnnotationMetadata importingClassMetadata)方法,这个方法将特定的类注入IOC容器,返回值封装的是这些类的全类名。 META-INF/spring.factories(老版本) META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. springboot在启动时会加载上面这两个文件的信息,这些信息最终会封装到String[]中。
-
@ComponentScan
: 开启组件扫描,指定 Spring 在哪些包下寻找组件类,以及其他带有@Component
注解的类。
springboot会根据@Conditional注解条件装配
@Conditional
@Conditional
是 Spring 框架中用于进行条件化配置的注解之一。它可以用在配置类上,也可以用在方法上,用于根据条件决定是否创建某个 Bean 或者是否加载某个配置。
自定义starter
在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot的 starter。
配置文件
springboot支持properties和yml/yaml配置文件
properties
在 Spring Boot 中,application.properties
或 application.yml
用于配置应用程序的属性。这些属性可以影响应用程序的行为,例如数据库连接、服务器端口、日志级别等。这种外部化配置的方式允许在不重新编译代码的情况下调整应用程序的行为。
# 服务器端口
server.port=8080
# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=user
spring.datasource.password=pass
# 日志级别
logging.level.root=info
yml
yml是一种可读的数据序列化格式,常用于配置文件和数据交换的场景。
优点:容易阅读,容易与脚本语言交互,以数据为核心,重数据轻格式
基本语法规则:大小写敏感
-
缩进: YAML 使用空格而非制表符进行缩进,缩进的空格数量必须一致。
-
键值对: 使用冒号
:
分隔键和值,键值对的后面要有一个空格。 -
数组: 使用短横线
-
表示数组项,每一项需要与前一项对齐。 -
注释: 使用
#
符号表示注释,从该符号到行尾的内容都被视为注释。 -
引用: 可以使用
&
和*
符号进行引用和别名,使得相同的数据可以在不同的地方引用。
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: user
password: pass
logging:
level:
root: info
优先级
在应用程序中,可以使用 @Value
注解或 @ConfigurationProperties
注解来注入这些属性值到相应的 Java 类中
@Component
public class MyComponent {
@Value("${server.port}")
private int serverPort;
@Value("${spring.datasource.url}")
private String databaseUrl;
}
@Component
@ConfigurationProperties(prefix = "server")
public class MyComponent {
private int port;
}
@Value
是 Spring Framework 中用于从配置文件中读取属性值的注解。它可以用于将外部配置文件中的值注入到 Spring 组件(如 bean 或字段)中。该注解支持 SpEL(Spring Expression Language)表达式,允许更灵活地处理属性值。具体用法为:@Value("${配置文件中的key}")
- 如果配置属性是基本数据类型(如 int、boolean),Spring 会尝试进行类型转换。
@Value
注解可以用于构造函数、字段、方法等地方。- 如果属性值中包含特殊字符,可以使用单引号括起来,例如
my.property='value with special characters'
。 - 如果需要在多个地方引用相同的属性值,可以使用
@Value
注解结合@ConfigurationProperties
注解。
@ConfigurationProperties
是 Spring Boot 中用于将配置文件中的属性值绑定到 Java 对象上的注解。通过使用该注解,可以方便地将配置文件中的属性映射到一个具体的 Java 类中,从而更好地组织和管理配置信息。prefix是key的前缀
@Value注解只能一个一个的进行外部属性的注入。
@ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中
多环境开发配置
yml
properties
多环境启动命令格式
带参数启动SpringBoot
- java -jar springboot.jar --spring.profiles.active=test
- java -jar springboot.jar --server.port=88
- java -jar springboot.jar --server.port=88 --spring.profiles.active=test
多环境开发兼容问题
Maven与SpringBoot关联操作(多环境配置),Maven设置优先级高于SpringBoot
配置文件分类
整合Junit
@SpringBootTest
类型:测试类注解
位置:测试类定义上方
作用:设置JUnit加载的SpringBoot启动类
相关属性classes:设置SpringBoot启动类
- @SpringBootTest(classes = Springboot07JunitApplication.class)
- class Springboot07JunitApplicationTests {}
注意事项:如果测试类在SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定
断言(Assertion)是一种用于测试代码中假设的机制。它们通常用于在开发和调试阶段验证代码的正确性,确保代码按照预期运行。断言可以帮助开发者捕获潜在的错误和问题,提供有助于定位问题的信息。
整合MyBatis
Spring cache
Spring Cache 提供了一种简单的声明式缓存机制,可以通过注解来缓存方法的结果,减少数据库查询或其他昂贵操作的次数。下面是如何在Spring Boot项目中配置和使用Spring Cache的步骤。
Spring Cache 底层是通过 AOP(面向切面编程)实现了基于注解的缓存功能。它通过 Spring AOP 拦截方法调用,在方法调用之前或之后执行缓存逻辑,从而实现自动缓存管理。
先看一下我们使用缓存步骤:
- 查寻缓存中是否存在数据,如果存在则直接返回结果
- 如果不存在则查询数据库,查询出结果后将结果存入缓存并返回结果
- 数据更新时,先更新数据库
- 然后更新缓存,或者直接删除缓存
此时我们会发现一个问题,所有我们需要使用缓存的地方都必须按照这个步骤去书写,这样就会出现很多逻辑上相似的代码。并且我们程序里面也需要显示的去调用第三方的缓存中间件的API,如此一来就大大的增加了我们项目和第三方中间件的耦合度。
使用
Redis
作为缓存中间件来进行缓存的实列,我们不难发现,我们的查询和存储时都是使用到了SpringBoot
整合Redis
后的相关API的,并且项目中所有的使用缓存的地方都会如此使用,这样子提升了代码的复杂度,我们程序员更应该关注的是业务代码,因此我们需要将查询缓存和存入缓存这类似的代码封装起来用框架来替我们实现,让我们更好的去处理业务逻辑。
首先,需要在 pom.xml
文件中添加 Spring Cache 和缓存实现(如EhCache、Caffeine等)的依赖。
<dependencies>
<!-- Spring Boot Starter Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 缓存实现,例如EhCache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
在Spring Boot应用的主类上启用缓存:@EnableCaching
以下是Spring Cache中常用的注解及其使用方法:
@Cacheable
@Cacheable
用于标记需要缓存结果的方法。当方法被调用时,Spring Cache会检查缓存是否有值。如果有,则返回缓存值;否则,调用方法并将结果存入缓存。
@Cacheable(value = "users", key = "#id")//SpEL表达式
public User getUserById(Long id) {
// 假设这是一个耗时的数据库查询操作
return findUserByIdFromDatabase(id);
}
@CachePut
@CachePut
用于标记既需要执行方法又需要将结果存入缓存的方法。与 @Cacheable
不同,@CachePut
不会跳过方法的执行。
@Caching
@Caching
用于组合多个缓存注解,可以在一个方法上应用多个缓存操作。
@Caching(
cacheable = { @Cacheable(value = "users", key = "#id") },
put = { @CachePut(value = "users", key = "#result.id") }
)
public User getUserAndUpdateCache(Long id) {
// 获取用户并更新缓存
return findUserByIdFromDatabase(id);
}
@CacheEvict
@CacheEvict
用于从缓存中移除一个或多个条目。它可以用于删除特定缓存条目或清空整个缓存。
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CacheEvict(value = "users", key = "#id")
public void deleteUserById(Long id) {
// 删除数据库中的用户
deleteUserFromDatabase(id);
}
private void deleteUserFromDatabase(Long id) {
// 模拟数据库删除
}
@CacheEvict(value = "users", allEntries = true)
public void deleteAllUsers() {
// 删除所有用户
}
}
@CacheConfig
@CacheConfig
用于类级别的缓存配置,减少重复配置。
@Service
@CacheConfig(cacheNames = "users")
public class UserService {
@Cacheable(key = "#id")
public User getUserById(Long id) {
// 假设这是一个耗时的数据库查询操作
return findUserByIdFromDatabase(id);
}
private User findUserByIdFromDatabase(Long id) {
// 模拟数据库查询
User user = new User();
user.setId(id);
user.setName("User " + id);
return user;
}
}
@Cacheable 必须要有返回【实体、list、map】-- 用于 查询
@CachePut 必须要有返回【实体、list、map】-- 用于 新增、修改
@CacheEvict 返回值为void--用于 删除
Spring-Cache的不足
SpringCache对读模式都进行处理,解决了缓存击穿,缓存穿透,缓存雪崩的问题,但是对写模式并没有去处理
读模式(SpringCache都处理了)
缓存穿透:查询一个null数据。 解决方法:缓存空数据。
spring.cache.redis.cache-null-values=true
缓存击穿:大量并发进来,查询一个正好过期的数据。 解决方法:加锁 :默认是无加锁的;@Cacheable(sync = true)
,加锁`(加锁,解决缓存击穿)缓存雪崩:大量的key同时过期 解决方法:加随机时间 (很容易弄巧成拙,要注意)
spring.cache.redis.time-to-live=3600000
以ms为单位 3600000为1小时写模式(SpringCache没有管)
我们该如何解决(3种方式)
- 引入中间件
Canal
,感知到mysql
的更新去更新- 读多写多的,直接去数据库查询
Spring-Cache小结
1、对于常规数据(读多写少,及时性、一致性要求不高的数据)完全可以使用 Spring Cache
2、对于特殊数据(比如要求高一致性)则需要特殊处理
下面是 Spring Cache 工作流程的详细介绍:
核心组件
- CacheManager:缓存管理器,负责管理不同的缓存实例。
- Cache:具体的缓存实例,提供缓存的基本操作,如
get
、put
、evict
等。 - CacheResolver:缓存解析器,负责解析要使用的缓存实例。
- KeyGenerator:缓存键生成器,负责生成缓存的键。
- CacheAspectSupport:缓存切面支持类,提供了缓存操作的核心逻辑。
主要流程
-
启用缓存:
- 通过
@EnableCaching
注解启用缓存支持。Spring 会自动配置缓存管理器和相关组件。
- 通过
-
方法拦截:
- 使用 AOP 切面拦截标有缓存注解的方法调用,如
@Cacheable
、@CachePut
和@CacheEvict
。 -
Spring 使用 AOP 切面拦截被缓存注解标记的方法调用。这由
CacheAspectSupport
类来实现。
- 使用 AOP 切面拦截标有缓存注解的方法调用,如
-
注解解析:
- 解析缓存注解,确定缓存的名称、键、条件等。
- 缓存注解的解析由
CacheOperationSource
接口实现,常见的实现类是AnnotationCacheOperationSource
。它负责解析方法上的缓存注解并生成缓存操作对象
-
缓存操作:
- 根据注解解析结果,执行相应的缓存操作,如读取缓存、更新缓存或清除缓存。
-
在
@Cacheable:在方法调用前检查缓存,如果缓存中有值则返回缓存值,否则调用方法并将结果存入缓存。@CachePut:在方法调用后将结果存入缓存。@CacheEvict:在方法调用后从缓存中移除指定条目。CacheAspectSupport
中,根据解析出的缓存操作执行相应的缓存逻辑:
Spring Task
Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑
在Spring Boot中,使用任务调度器(Task Scheduler)可以很方便地实现定时任务。下面是如何配置和使用Spring Task的详细步骤。
添加依赖,在pom.xml
中添加了Spring Task的依赖
启用定时任务,在你的Spring Boot应用程序主类或配置类上添加@EnableScheduling
注解,以启用定时任务
定义定时任务,使用@Scheduled
注解来定义定时任务。
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MyScheduledTasks {
@Async
@Scheduled(fixedRate = 5000)
public void scheduleFixedRateTask() {
System.out.println("Fixed rate task - " + System.currentTimeMillis() / 1000);
}
@Async
@Scheduled(fixedDelay = 10000)
public void scheduleFixedDelayTask() {
System.out.println("Fixed delay task - " + System.currentTimeMillis() / 1000);
}
@Async
@Scheduled(cron = "0 0 0 * * ?")
public void scheduleCronTask() {
System.out.println("Cron task - " + System.currentTimeMillis() / 1000);
}
}
详细说明
@Scheduled(fixedRate = 5000)
: 每隔5秒执行一次任务,不管上一次任务是否完成。@Scheduled(fixedDelay = 10000)
: 每隔10秒执行一次任务,但等待上一次任务完成后再开始。@Scheduled(cron = "0 0 0 * * ?")
: 使用cron表达式在每天午夜执行任务。
配置并发任务
如果你有多个任务需要并发执行,可以使用@Async
注解。首先,需要启用异步支持:
在你的Spring Boot应用程序主类或配置类上添加@EnableAsync注解,以启用定时任务
然后在任务方法上添加@Async
注解
配置线程池,为了更好地管理并发任务,可以配置一个线程池。在任务上使用@Async("xxxExecutor")
WebSocket
WebSocket 是一种在 Web 应用程序中提供双向通信的协议,它允许在浏览器和服务器之间建立持久连接,以便实时地传输数据。与传统的 HTTP 请求-响应模式不同,WebSocket 允许服务器主动向客户端推送数据,而无需客户端发起请求。
握手是基于http协议的,传输基于TCP。
特点和优势:
- 双向通信:WebSocket 提供了全双工通信,客户端和服务器可以同时发送和接收数据,实现了实时的双向通信。
- 持久连接:WebSocket 在客户端和服务器之间建立持久连接,避免了 HTTP 协议中频繁建立和断开连接的开销。
- 低延迟:由于建立了持久连接,WebSocket 可以实现低延迟的实时通信,适用于需要快速响应和实时更新的应用场景。
- 减少数据传输量:与轮询和长轮询等技术相比,WebSocket 采用了更高效的数据传输方式,可以减少通信的开销和带宽的消耗。
- 跨域支持:WebSocket 协议支持跨域通信,允许浏览器与不同域上的服务器建立连接,从而实现跨域通信。
使用方式:
- 建立连接:客户端通过 JavaScript 的 WebSocket API 发起连接请求,向服务器发送 WebSocket 握手请求。
- 握手过程:服务器接收到握手请求后,进行握手确认,建立 WebSocket 连接。此时,客户端和服务器之间的连接就建立了,可以进行双向通信。
- 数据传输:客户端和服务器可以通过发送消息进行数据传输,消息可以是文本、二进制数据等。
- 关闭连接:客户端或服务器可以随时关闭 WebSocket 连接,结束通信。
应用场景:
- 实时聊天应用:WebSocket 可以实现实时聊天功能,用户可以即时发送和接收消息。
- 实时数据更新:用于实时更新数据的应用程序,如股票行情、即时通知等。
- 在线游戏:WebSocket 可以实现实时多人在线游戏,支持实时交互和同步。
- 协作应用:用于协作编辑、远程控制等场景,可以实时同步用户的操作和状态。
- 监控和通知:用于监控系统状态、实时通知和警报等应用。
总结(总体过程):
- 首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
- 然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
- 最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。
- WebSocket-CSDN博客
Java Bean Validation
Java Bean Validation API 是一套用于验证 Java 对象属性的标准规范。 API 是一套用于验证 Java 对象属性的标准规范。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
常用约束注解
-
基本注解:
@Null
:验证对象是否为 null。@NotNull
:验证对象是否不为 null。@AssertTrue
:验证布尔值是否为 true。@AssertFalse
:验证布尔值是否为 false。@Min(value)
:验证数字是否大于等于指定的最小值。@Max(value)
:验证数字是否小于等于指定的最大值。@Size(min, max)
:验证字符串、集合、数组等长度是否在指定范围内。@Pattern(regexp)
:验证字符串是否符合指定的正则表达式。@Email
:验证字符串是否是一个有效的电子邮件地址。
-
日期相关:
@Past
:验证日期是否在当前日期之前。@PastOrPresent
:验证日期是否在当前日期或之前。@Future
:验证日期是否在当前日期之后。@FutureOrPresent
:验证日期是否在当前日期或之后。
import javax.validation.constraints.*;
public class User {
@NotNull(message = "Name cannot be null")
@Size(min = 2, max = 30, message = "Name must be between 2 and 30 characters")
private String name;
@Min(value = 18, message = "Age should not be less than 18")
private int age;
@Email(message = "Email should be valid")
private String email;
// getters and setters
}
@Valid
注解用于在Spring中对对象进行校验。
在控制器的方法参数中使用@Valid
注解,可以对传入的请求体进行校验。如果校验失败,会抛出MethodArgumentNotValidException
,可以通过@ExceptionHandler
或@ControllerAdvice
处理异常。
public ResponseEntity<String> createUser(@Valid @RequestBody User user, BindingResult result)
@Pattern
注解的使用
@Pattern
注解可以与其他验证注解一起使用,例如 @NotBlank
、@Size
等,以确保字段不仅符合正则表达式,还满足其他约束条件。
常用属性
regexp
:指定要匹配的正则表达式。flags
:指定匹配时使用的标志,例如Pattern.CASE_INSENSITIVE
表示忽略大小写。message
:指定验证失败时的错误消息。