Spring Boot 知识点
约定优于配置
Spring 和 SpringBoot
Spring 缺点:重配置
Spring Boot 优点
- 起步依赖:将多个 maven 包的坐标整合到一起,并提供一些默认的功能
- 自动配置:会自动将一些配置类的 bean 注册进 IoC 容器
单元测试
- @SpringBootTest:标记该类为 SpringBoot 单元测试类,并加载 applicationContext 上下文环境
- @RunWith (SpringRunner.class):测试启动器,并加载 SpringBoot 测试注解
热部署
依赖
1 | <dependency> |
- IDEA 开启自动编译
- Ctrl + Shift + Alt + /-> Registry -> compiler.automake.allow.when.app.running √:用于指定 IDEA 在程序运行过程中自动编译
配置
5.1 ConfigurationProperties
依赖包
1 | <dependency> |
- [@ConfigurationProperties(prefix ](/ConfigurationProperties(prefix ) = "person"):将配置文件中以 person 开头的属性注入到类中
1 |
|
-
- 无需 set 方法
- 不支持 Map、对象及行内写法
1
2
3
4
5
public class Person {
private int id;
}
@PropertySource:加载配置文件
1
2
3
4
5
6
7
public class Person {
private int id;
// set and get
}随机值设置
使用 Spring Boot 内嵌的 RandomValuePropertySource 类1
2
3
4
5
6
7
8my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
# 配置小于 10 的随机整数
my.number.less.than.ten=${random.int(10)}
# 配置范围在 [1024,65536] 之间的随机整数
my.number.in.range=${random.int[1024,65536]}参数引用
1
2app.name=MyApp
app.description=${app.name} is a Spring Boot application
@SpringBootApplication 源码
1 |
|
@SpringBootConfiguration
组合了 @Configuration,配置类1
2
3
4
5
6
7
// 配置 IoC 容器
public SpringBootConfiguration {
}@EnableAutoConfiguration
- @AutoConfigurationPackage:将 AutoConfigurationPackages.Registrar 类导入到 IoC 容器中。拿到启动类所在的包名
- @Import (AutoConfigurationImportSelector.class) :将 AutoConfigurationImportSelector 类导入到 IoC 容器中。
- AutoConfigurationMetadataLoader#loadMetadata:加载 spring-boot-autoconfigure.jar 依赖包中
spring-autoconfigure-metadata.properties
文件中的属性,其定义了各个自动配置类及其加载条件。最终生成 AutoConfigurationMetadata 对象。 - AutoConfigurationImportSelector#getAutoConfigurationEntry:
- AutoConfigurationImportSelector#getAttributes:获得注解的属性
- AutoConfigurationImportSelector#getCandidateConfigurations: 获取默认支持的自动配置类名列表
Spring Boot 在启动的时候,使用内部工具类 SpringFactoriesLoader 查找 classpath 上所有 jar 包中的META-INF/spring.factories
文件。找到将 key 为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的键值对,其值为多个工厂类名称。取出生成自动配置类名列表
。 - AutoConfigurationImportSelector#removeDuplicates:对
自动配置类名列表
去重 - AutoConfigurationImportSelector#getExclusions:得到需排除的自动配置类,后续操作对列表集合进行排除
- AutoConfigurationImportSelector#filter:根据配置类的 @ConditionalXXX 注解指定的条件对列表集合进行筛选,并对 AutoConfigurationMetadataLoader#loadMetadata 生成的 AutoConfigurationMetadata 对象进行筛选
- AutoConfigurationImportSelector#fireAutoConfigurationImportEvents:将自动配置导入事件通知监听器。
过滤完成后会自动加载类路径下 Jar 包中META-INF/spring.factories
文件中 AutoConfigurationImportListener 的实现类,并触发 fireAutoConfigurationImportEvents 事件。进行自动配置类的加载创建
- AutoConfigurationMetadataLoader#loadMetadata:加载 spring-boot-autoconfigure.jar 依赖包中
1 |
|
自定义 starter
导入依赖
1
2
3
4
5
6
7<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>SimpleBean
1
2
3
4
5
6
7
public class SimpleBean {
private int id;
private String name;
// setter and getter
}MyAutoConfiguration
@ConditionalOnClass:当类路径 classpath 下有指定的类的情况下才进行自动配置1
2
3
4
5
6
7
8
9
10
public class MyAutoConfiguration {
static {
System.out.println("MyAutoConfiguration init....");
}
public SimpleBean simpleBean(){
return new SimpleBean();
}
}resources 下创建 /META-INF/spring.factories
1
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.ccomma.config.MyAutoConfiguration
SpringApplication#run 源码
- 静态方法 SpringApplication#run
- 创建 SpringApplication 实例:new SpringApplication (primarySources),其中 primarySources 为启动类的 Class 对象
- 存储项目启动类的 Class 对象至属性字段中
- 设置应用类型是 SERVLET 应用(Spring 5 之前的传统 MVC 应用)还是 REACTIVE 应用(Spring 5 开始出现的 WebFlux 交互式应用)
- SpringApplication#getSpringFactoriesInstances (ApplicationContextInitializer.class):在
spring.factories
中获取以 ApplicationContextInitializer 全类名为键的所有值(值为初始化器的全类名),并创建实例返回。即返回了 ApplicationContextInitializer 这一类的所有初始化器实例。然后进行设置 - SpringApplication#getSpringFactoriesInstances (ApplicationListener.class):同上,获取 ApplicationListener 这一键下的所有监听器并进行设置
- 初始化 mainApplicationClass 属性:用于推断并设置项目 main () 方法启动的主程序启动类
- 调用 SpringApplication 实例的 run 方法
- 获取并启动监听器:调用 SpringApplication#getRunListeners 方法从
spring.factories
中获取键为 SpringApplicationRunListener.class 的所有值(全类名),放入 SpringApplicationRunListeners 对象中,并调用其 starting 方法启动监听器 - 项目运行环境 Environment 的预配置:调用 SpringApplication#prepareEnvironment 方法创建并配置当前 SpringBoot 应用将要使用的 Environment,并遍历调用所有的 SpringApplicationRunListener 的 environmentPrepared () 方法。最后调用 SpringApplication#configureIgnoreBeanInfo 配置环境
- SpringApplication#getOrCreateEnvironment:获取或创建环境
- SpringApplication#configureEnvironment:配置环境。配置 PropertySources 和 Active Profiles
- SpringApplicationRunListeners#environmentPrepared:Listeners 环境准备(广播 ApplicationEnvironmentPreparedEvent 事件)
- SpringApplication#bindToSpringApplication:将环境绑定到 SpringApplication 中
- 创建 Spring 容器:调用 SpringApplication#createApplicationContext 创建一个 AnnotationConfigServletWebServerApplicationContext 容器
- 获得异常报告器(SpringBootExceptionReporter 集合):调用 SpringApplication#getSpringFactoriesInstances 从
spring.factories
中获取键为 BootExceptionReporter.class 的所有类,即所有异常报告器 - Spring 容器前置处理:调用 SpringApplication#prepareContext 方法,在容器刷新之前的准备动作,比如触发监听器的响应事件、加载资源、设置上下文环境等。并将启动类注入容器为后续开启自动化配置奠定基础
- SpringApplication#load:将启动类注入容器,为后续开启自动化配置奠定基础
- 刷新容器:调用 SpringApplication#refreshContext
- SpringApplication#refresh:初始化 IoC 容器,包括 Bean 资源的定位、解析、注册等
- ConfigurableApplicationContext#registerShutdownHook:注册 JVM 关闭钩子。在 JVM 关机时关闭这个上下文
- Spring 容器后置处理:钩子,无实现可拓展
- 发出结束执行的事件通知:调用 SpringApplicationRunListeners#started 方法执行所有监听器的 SpringApplicationRunListener#started 方法
- SpringApplication#callRunners:获取并执行 ApplicationRunner 和 CommandLineRunner 的所有实现类。Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。Spring Boot 提供了 ApplicationRunner 和 CommandLineRunner 两种服务接口
- 发布应用上下文就绪事件:调用 SpringApplicationRunListeners#running 方法来持续运行配置好的应用上下文 ApplicationContext
- 获取并启动监听器:调用 SpringApplication#getRunListeners 方法从
- 创建 SpringApplication 实例:new SpringApplication (primarySources),其中 primarySources 为启动类的 Class 对象
缓存管理
- 注解方式:未使用缓存管理器则默认使用 Simple 缓存组件进行缓存
-
1
2
3
4
5
6
7
8
public class Springboot04CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot04CacheApplication.class, args);
}
} @Cacheable:缓存数据,用于查询方法
-
1 |
|
- @CachePut:更新数据库后更新缓存,用于更新方法
- @CacheEvict:删除数据库数据后删除缓存,用于删除方法
API 方式
- RedisTemplate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ApiCommentService {
private CommentRepository commentRepository;
private RedisTemplate redisTemplate;
public Comment findCommentById(Integer id) {
Object o = redisTemplate.opsForValue().get("comment_" + id);
if (o != null) {
return (Comment) o;
} else {
// 缓存中没有,从数据库中查询
Optional<Comment> byId = commentRepository.findById(id);
if (byId.isPresent()) {
Comment comment = byId.get();
// 将查询结果存入到缓存中
redisTemplate.opsForValue().set("comment_"+id,comment,1,TimeUnit.DAYS);
return comment;
} else {
return null;
}
}
}
public Comment updateComment(Comment comment) {
commentRepository.updateComment(comment.getAuthor(), comment.getaId());
// 更新数据后进行缓存更新
redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
return comment;
}
public void deleteComment(int comment_id) {
commentRepository.deleteById(comment_id);
redisTemplate.delete("comment_"+comment_id);
}
}
- RedisTemplate
自定义 Redis 缓存序列化机制
- RedisTemplate#afterPropertiesSet 方法中判断如果默认序列化参数 defaultSerializer 为空,则指定 JdkSerializationRedisSerializer 为默认序列化方式,所以进行缓存的实体类必须实现 Serializable 接口
- RedisConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RedisConfig {
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
// 使用 Json 格式序列化对象,对缓存数据 key 和 value 进行转换
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 设置 RedisTemplate 模版 API 的序列化方式为 Jsonz
template.setDefaultSerializer(jacksonSeial); return template;
}
}