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
 8- my.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 
 2- app.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;
 }
 }
 
