1 自动配置

1.1 自动装配简介

1.1.1 概述

  • 在早期使用 Spring 开发框架的时候,如果要想进行某些服务的整合,常规的做法就是引入服务有关的依赖库,而后配置一些 XML 文件以及进行组件的定义,但是在 SpringBoot 中会出现很多的 starter 。
  • 项目的开发要求是不断进化的,随着时间以及技术的推移,一个项目中除了基本的编程语言之外,还需要进行大量的应用服务整合,例如:在项目中会使用到 MySQL 数据库进行持久化存储,同时会利用 Redis 实现分布式缓存,以及使用 RabbitMQ 实现异构系统整合服务,这些都需要通过 Gradle 构建工具引入相关的依赖库之后才可以整合到项目之中,为项目提供应有的服务支持。

1.png

  • 在之前曾经引入过 Redis 的相关依赖,本次继续通过这个依赖来观察。

1.1.2 microboot 项目

  • 修改 build.gradle 的配置,引入 Redis 的依赖:
  1. project(':microboot-web') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation(project(':microboot-common')) // 引入其他子模块
  4. // 引入 SpringBoot 的 web 依赖
  5. implementation 'org.springframework.boot:spring-boot-starter-web'
  6. implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis'
  7. implementation group: 'org.apache.commons', name: 'commons-pool2'
  8. }
  9. // processResources {
  10. // apply from: "src/main/profiles/${env}/profile.gradle"
  11. // rootProject.ext["env"] = env
  12. // expand(project.properties)
  13. // }
  14. gradle.taskGraph.whenReady { // 在所有的操作准备好之后触发
  15. tasks.each { task ->
  16. if (task.name.contains('javadoc')) { // 如果发现有 javadoc 任务,就跳过
  17. task.enabled = false // 当前任务不执行
  18. }
  19. }
  20. }
  21. }

1.1.3 microboot-web 子模块

  • 一旦在项目之中引入相关的 starter ,那么就会出现一系列的自动配置处理类,如:在 Redis 里面它所提供的自动配置类 RedisAutoConfiguration 。
  1. package org.springframework.boot.autoconfigure.data.redis;
  2. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  3. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  4. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  5. import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
  6. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.context.annotation.Import;
  10. import org.springframework.data.redis.connection.RedisConnectionFactory;
  11. import org.springframework.data.redis.core.RedisOperations;
  12. import org.springframework.data.redis.core.RedisTemplate;
  13. import org.springframework.data.redis.core.StringRedisTemplate;
  14. @Configuration(proxyBeanMethods = false) // 定义配置 Bean
  15. @ConditionalOnClass(RedisOperations.class) // 在当前应用中存在 RedisOperations 类的时候可以初始化
  16. @EnableConfigurationProperties(RedisProperties.class) // 配置的属性(application.yml)
  17. @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
  18. public class RedisAutoConfiguration {
  19. @Bean // 向容器中注册 Bean
  20. @ConditionalOnMissingBean(name = "redisTemplate") // 在没有 redisTemplate 的时候注册 Bean
  21. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
  22. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  23. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  24. template.setConnectionFactory(redisConnectionFactory);
  25. return template;
  26. }
  27. @Bean // 向容器中注册 Bean
  28. @ConditionalOnMissingBean
  29. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
  30. public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
  31. StringRedisTemplate template = new StringRedisTemplate();
  32. template.setConnectionFactory(redisConnectionFactory);
  33. return template;
  34. }
  35. }
  • 不同的模块定义一些自动配置类,而后这些配置类需要结合 application.yml 配置生效,实现最终的 Bean 的注册,当注册完毕后就可以在 Spring 容器中直接使用了。

1.2 @EnableConfigurationProperties 注解

1.2.1 概述

  • 在前面的章节中讲解了如果基于配置文件(application.yml)实现 Bean 注册的操作管理,但是在 SpringBoot 里面实际上还存在有一个自动装配的注解,这个注解就是本次所要讲解的 @EnableConfigurationProperties 注解。

1.2.2 microboot 项目

  • 考虑到后面会有一系列的模块整合的操作,此时需要创建一个新的模块 microboot-autoconfig-starter ,随后编辑 buid.gradle 文件进行核心依赖库的配置:
  1. project(':microboot-autoconfig-starter') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation 'org.springframework.boot:spring-boot-starter-web'
  4. // spring-boot-configuration-processor
  5. annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
  6. }
  7. }

1.2.3 microboot-autoconfig-starter 子模块

  • 既然要实现 Bean 注册,那么首先需要创建一个类,然后设计其配置的前缀的信息项:
  1. package com.github.fairy.era.vo;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2022-01-18 14:04
  8. */
  9. @Data
  10. @ConfigurationProperties(prefix = "dept")
  11. public class Dept {
  12. private String deptNo;
  13. private String deptName;
  14. private String location;
  15. }
  • 按照以前的讲解,如果一个 Bean 类的定义上出现了 @ConfigurationProperties 注解,那么就应该定义为一个组件,否则程序将出现错误,但是此时的配置的启用并不是通过 @Component 注解完成的,而是通过另外的注解实现的,所以即使此时有错误,也请先搁置。
  • 创建一个自动的装配类:
  1. package com.github.fairy.era.config;
  2. import com.github.fairy.era.vo.Dept;
  3. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  4. import org.springframework.context.annotation.Configuration;
  5. /**
  6. * 自动装配类
  7. *
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-18 14:14
  11. */
  12. @Configuration
  13. @EnableConfigurationProperties(Dept.class)
  14. public class XxxAutoConfiguration {
  15. }
  • 为了便于理解,我们在这个模块中编写一个启动类:
  1. package com.github.fairy.era;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2022-01-18 14:16
  8. */
  9. @SpringBootApplication
  10. public class Application {
  11. public static void main(String[] args) {
  12. SpringApplication.run(Application.class, args);
  13. }
  14. }
  • 创建 application.yml 配置,进行属性的定义:
  1. dept:
  2. dept-no: 1
  3. dept-name: 开发部
  4. location: 上海
  • 编写测试类,看是否能进行 Dept 对象的注入:
  1. package com.github.fairy.era;
  2. import com.github.fairy.era.vo.Dept;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. /**
  7. * @author 许大仙
  8. * @version 1.0
  9. * @since 2022-01-18 14:18
  10. */
  11. @SpringBootTest
  12. public class ApplicationTest {
  13. @Autowired
  14. private Dept dept;
  15. @Test
  16. public void test() {
  17. System.out.println("dept = " + dept);
  18. }
  19. }
  • 控制台信息输出:

2.png

  • 此时程序之中所有对应的 Bean 的属性内容就已经成功的保存。

1.3 @Import 注解

1.3.1 概述

  • 之前已经分析过了 @EnableConfigurationProperties 注解的使用,下面需要查看这个注解的源代码:
  1. package org.springframework.boot.context.properties;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Import;
  9. @Target(ElementType.TYPE)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Documented
  12. @Import(EnableConfigurationPropertiesRegistrar.class) // @Import 注解
  13. public @interface EnableConfigurationProperties {
  14. String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
  15. Class<?>[] value() default {};
  16. }
  • 可以知道,此时使用 @Import 注解就是将 Bean 注册到 Spring 的容器中,需要注意的是,@Import 注解支持三种不同的处理形式:类导入、ImportSelector 导入 、ImportBeanDefinitionRegistrar 导入。
  1. package org.springframework.context.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. @Target(ElementType.TYPE)
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface Import { // 此注解相当于早期 XML 时代的 <import/>标签,此注解也可以进行大面积 Bean 的导入
  11. /**
  12. * {@link Configuration @Configuration}, {@link ImportSelector},
  13. * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
  14. */
  15. Class<?>[] value();
  16. }

1.3.2 方式一

  • 直接进行 Bean 的导入:
  1. package com.github.fairy.era.config;
  2. import com.github.fairy.era.vo.Dept;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. /**
  6. * 自动装配类
  7. *
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-18 14:14
  11. */
  12. @Configuration
  13. @Import(Dept.class)
  14. public class XxxAutoConfiguration {
  15. }
  • 程序执行结果:
  1. dept = Dept(deptNo=1, deptName=开发部, location=上海)
  • 对于此时的 Dept 类来说,并没有实现任何的 Bean 定义,但是由于使用了 @Import 注解,所以也实现了 Bean 的注册。

1.3.3 方式二

  • 如果此时需要注入的 Bean 很多,如果按照上面的方式去编写就需要写上所有注册的 Bean 的名称,这样就可以使用 ImportSelector 来进行简化。
  1. package com.github.fairy.era.selector;
  2. import org.springframework.context.annotation.ImportSelector;
  3. import org.springframework.core.type.AnnotationMetadata;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2022-01-18 14:50
  8. */
  9. public class XxxImportSelector implements ImportSelector {
  10. @Override
  11. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  12. return new String[]{"com.github.fairy.era.vo.Dept"}; // 导入类的全名称
  13. }
  14. }
  1. package com.github.fairy.era.config;
  2. import com.github.fairy.era.selector.XxxImportSelector;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. /**
  6. * 自动装配类
  7. *
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-18 14:14
  11. */
  12. @Configuration
  13. @Import(XxxImportSelector.class)
  14. public class XxxAutoConfiguration {
  15. }
  • 程序执行结果:
  1. dept = Dept(deptNo=1, deptName=开发部, location=上海)

1.3.4 方式三

  • 以上的操作实际上都是由 Spring 容器负责了 Bean 的注册,如果开发者希望可以自己来进行一些 Bean 注册处理,则可以使用 ImportBeanDefinitionRegistrar 。
  1. package com.github.fairy.era.registrar;
  2. import com.github.fairy.era.vo.Dept;
  3. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  4. import org.springframework.beans.factory.support.RootBeanDefinition;
  5. import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
  6. import org.springframework.core.type.AnnotationMetadata;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-18 15:01
  11. */
  12. public class DefaultImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  13. @Override
  14. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  15. RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Dept.class);
  16. registry.registerBeanDefinition("dept", rootBeanDefinition);
  17. }
  18. }
  1. package com.github.fairy.era.config;
  2. import com.github.fairy.era.registrar.DefaultImportBeanDefinitionRegistrar;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. /**
  6. * 自动装配类
  7. *
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2022-01-18 14:14
  11. */
  12. @Configuration
  13. @Import(DefaultImportBeanDefinitionRegistrar.class)
  14. public class XxxAutoConfiguration {
  15. }
  • 程序运行结果:
  1. dept = Dept(deptNo=1, deptName=开发部, location=上海)

1.4 自定义 starter 组件

1.4.1 概述

  • 在之前分析了很多的程序实现注解,包括自动提示配置,实际上最终的目的就是希望将当前的模块交由其他的模块去使用,这就是 starter 的目的所在。
  • 利用自动配置类可以在容器启动的时候自动的进行 Bean 的注册,由于不同的项目会存在不同的自动配置类,所以为了便于其他模块的引用,就需要将配置类在 spring.factories 之中进行注册管理。

1.4.2 microboot-autoconfig-starter 子模块

  • 考虑到整合的操作形式,建议多追加一个 Bean 的注册。
  1. package com.github.fairy.era.config;
  2. import com.github.fairy.era.vo.Dept;
  3. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. /**
  9. * 自动装配类
  10. *
  11. * @author 许大仙
  12. * @version 1.0
  13. * @since 2022-01-18 14:14
  14. */
  15. @Configuration
  16. @EnableConfigurationProperties(Dept.class)
  17. public class XxxAutoConfiguration {
  18. /**
  19. * 自定义注册 Bean 对象
  20. *
  21. * @return
  22. */
  23. @Bean(name="books")
  24. public List<String> books() {
  25. List<String> books = new ArrayList<>();
  26. books.add("Java 从入门到入土");
  27. books.add("C 从入门到精通");
  28. books.add("SpringBoot 就业编程实战");
  29. books.add("SpringCloud 从入门到精通");
  30. return books;
  31. }
  32. }
  • 如果要想让自动配置类生效,还需要一个特定的配置文件 src/main/resources/META/spring.factories :
  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. com.github.fairy.era.config.XxxAutoConfiguration
  • 修改 build.gradle 配置文件:
  1. plugins {
  2. id 'idea'
  3. id 'java'
  4. }
  5. jar {
  6. enabled = true // 允许当前的模块打包为 *.jar 文件
  7. }
  8. javadocTask {
  9. enabled = false // 不启用 javadoc 任务
  10. }
  11. javadocJar {
  12. enabled = false // 不启用 javadoc 的 *.jar 文件
  13. }
  14. bootJar {
  15. enabled = false // 不执行 SpringBoot 的打包任务
  16. }

1.4.3 microboot 项目

  • 修改 build.gradle 配置文件,为 microboot-web 子模块添加新的依赖支持:
  1. project(':microboot-web') { // 设置子项目的配置,独享配置
  2. dependencies { // 配置子模块依赖
  3. implementation(project(':microboot-common')) // 引入其他子模块
  4. implementation(project(':microboot-autoconfig-starter')) // 引入其他子模块
  5. // 引入 SpringBoot 的 web 依赖
  6. implementation 'org.springframework.boot:spring-boot-starter-web'
  7. }
  8. gradle.taskGraph.whenReady { // 在所有的操作准备好之后触发
  9. tasks.each { task ->
  10. if (task.name.contains('javadoc')) { // 如果发现有 javadoc 任务,就跳过
  11. task.enabled = false // 当前任务不执行
  12. }
  13. }
  14. }
  15. }
  • 修改 application.yml 配置文件,给 Dept 对象设置属性:
  1. dept:
  2. dept-no: 1
  3. dept-name: 开发部
  4. location: 北京
  • 测试:
  1. package com.github.fairy.era;
  2. import org.junit.jupiter.api.Test;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.test.context.SpringBootTest;
  5. import java.util.List;
  6. /**
  7. * @author 许大仙
  8. * @version 1.0
  9. * @since 2022-01-04 09:36
  10. */
  11. @SpringBootTest
  12. public class ApplicationTest {
  13. @Autowired
  14. private List<String> books;
  15. @Test
  16. public void test() {
  17. System.out.println("books = " + books);
  18. }
  19. }
  • 程序执行结果:
  1. books = [Java 从入门到入土, C 从入门到精通, SpringBoot 就业编程实战, SpringCloud 从入门到精通]

2 SpringBoot 启动分析

2.1 SpringBoot 启动核心类

2.1.1 概述

  • 为什么在 SpringBoot 程序开发之中,只需要按照规定的结构(指的是包结构)进行代码的编写,就可以实现自动的装配处理;而早期的 Spring 开发框架往往需要通过手工配置的模式来进行扫描包的处理,此时就需要对 SpringBoot 的运行有一个基本的了解。
  • 只要是 SpringBoot 应用程序,都需要在根包下创建一个应用的启动类,而常用的启动类的形式如下:
  1. package com.github.fairy.era;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. /**
  5. * 启动类
  6. *
  7. * @author 许大仙
  8. * @version 1.0
  9. * @since 2021-12-31 09:14
  10. */
  11. @SpringBootApplication // 注解部分
  12. public class Application {
  13. public static void main(String[] args) {
  14. // 程序部分
  15. SpringApplication.run(Application.class, args);
  16. }
  17. }
  • 如果要去理解 SpringBoot 的启动流程,实际上就需要分为两个阶段来理解:第一个阶段是注解操作部分,另外一个阶段才是具体的程序的部分。

2.1.2 @SpringBootApplication 注解

  • 本次首先研究一个注解的基本使用形式,打开 @SpringBoot 注解的源代码来进行观察:
  1. package org.springframework.boot.autoconfigure;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. import org.springframework.beans.factory.support.BeanNameGenerator;
  9. import org.springframework.boot.SpringApplication;
  10. import org.springframework.boot.SpringBootConfiguration;
  11. import org.springframework.boot.context.TypeExcludeFilter;
  12. import org.springframework.context.annotation.AnnotationBeanNameGenerator;
  13. import org.springframework.context.annotation.Bean;
  14. import org.springframework.context.annotation.ComponentScan;
  15. import org.springframework.context.annotation.ComponentScan.Filter;
  16. import org.springframework.context.annotation.Configuration;
  17. import org.springframework.context.annotation.FilterType;
  18. import org.springframework.core.annotation.AliasFor;
  19. import org.springframework.data.repository.Repository; // 这边有错误
  20. @Target(ElementType.TYPE)
  21. @Retention(RetentionPolicy.RUNTIME)
  22. @Documented
  23. @Inherited
  24. @SpringBootConfiguration // SpringBoot 的配置注解
  25. @EnableAutoConfiguration // 启用自动配置
  26. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  27. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 配置扫描包
  28. public @interface SpringBootApplication {
  29. @AliasFor(annotation = EnableAutoConfiguration.class) // 定义别名
  30. Class<?>[] exclude() default {};
  31. @AliasFor(annotation = EnableAutoConfiguration.class) // 定义别名
  32. String[] excludeName() default {};
  33. @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") // 定义别名
  34. String[] scanBasePackages() default {}; // 定义扫描包
  35. @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") // 定义别名
  36. Class<?>[] scanBasePackageClasses() default {}; // 定义扫描类
  37. @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator") // 定义别名
  38. Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  39. @AliasFor(annotation = Configuration.class) // 定义别名
  40. boolean proxyBeanMethods() default true; // 扫描模式
  41. }
  • 在 SpringBoot 启动类的注解之中就会发现两个主要的注解:
    • @SpringBootConfiguration 注解:SpringBoot 的启动代理模式配置。
    • @EnableAutoConfiguration 注解:启用自动装配。

2.1.3 @SpringBootConfiguration 注解

  • 观察 @SpringBootConfiguration 注解,其源码如下:
  1. package org.springframework.boot;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.core.annotation.AliasFor;
  10. @Target(ElementType.TYPE)
  11. @Retention(RetentionPolicy.RUNTIME)
  12. @Documented
  13. @Configuration
  14. public @interface SpringBootConfiguration {
  15. @AliasFor(annotation = Configuration.class)
  16. boolean proxyBeanMethods() default true;
  17. }
  • 可以发现 @SpringBootConfiguration 注解和 @Configuration 等效,所以被 @SpringBootConfiguration 注解标注的类就是一个配置类。

2.1.4 @EnableAutoConfiguration 注解。

  • 观察 @EnableAutoConfiguration 注解,其源码如下:
  1. package org.springframework.boot.autoconfigure;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
  9. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  10. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  11. import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
  12. import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
  13. import org.springframework.context.annotation.Conditional;
  14. import org.springframework.context.annotation.Configuration;
  15. import org.springframework.context.annotation.Import;
  16. import org.springframework.core.io.support.SpringFactoriesLoader;
  17. @Target(ElementType.TYPE)
  18. @Retention(RetentionPolicy.RUNTIME)
  19. @Documented
  20. @Inherited
  21. @AutoConfigurationPackage // 自动配置包
  22. @Import(AutoConfigurationImportSelector.class) // 此时存在一个 Selector
  23. public @interface EnableAutoConfiguration {
  24. /**
  25. * 定义了一个所需要的环境属性名称
  26. */
  27. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  28. /**
  29. * 排除
  30. */
  31. Class<?>[] exclude() default {};
  32. /**
  33. * 排除
  34. */
  35. String[] excludeName() default {};
  36. }
  • 在这个注解之中可以发现有一个 @AutoConfigurationPackage(主要进行包扫描配置处理的) 和 @Import 的注解(导入的是 AutoConfigurationImportSelector)。

2.1.5 @AutoConfigurationPackage 注解

  • 观察 @AutoConfigurationPackage 注解,其源码如下:
  1. package org.springframework.boot.autoconfigure;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. import org.springframework.context.annotation.Import;
  9. @Target(ElementType.TYPE)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Documented
  12. @Inherited
  13. @Import(AutoConfigurationPackages.Registrar.class) // 设置了 Registrar 导入器
  14. public @interface AutoConfigurationPackage {
  15. String[] basePackages() default {};
  16. Class<?>[] basePackageClasses() default {};
  17. }
  • 可以发现自动导入了 Registrar 导入器,打开 AutoConfigurationPackages.Registrar 的源代码,内容如下:
  1. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  4. register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
  5. }
  6. @Override
  7. public Set<Object> determineImports(AnnotationMetadata metadata) {
  8. return Collections.singleton(new PackageImports(metadata));
  9. }
  10. }
  1. public static void register(BeanDefinitionRegistry registry, String... packageNames) {
  2. if (registry.containsBeanDefinition(BEAN)) { // 判断是否有指定 BEAN
  3. BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
  4. // 将 Bean 对应的包进行扫描配置
  5. beanDefinition.addBasePackages(packageNames);
  6. }
  7. else {
  8. registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
  9. }
  10. }
  1. private static final String BEAN = AutoConfigurationPackages.class.getName();
  • 通过上面的分析可以发现自动扫描的时候可以通过一系列的 Selector 实现最终扫描包的配置,但是如果要想继续研究需要观察 AutoConfigurationImportSelector 的源代码,这个类会集成大量的父类,如果要想研究其核心,肯定要以 Selector 为主,观察这个类的继承结构。
  1. package org.springframework.boot.autoconfigure;
  2. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  3. ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  4. // 其他略
  5. }
  • 此时重点观察的是 DeferredImportSelector 接口,因为该类主要用于 @Import 注解之中,所以只需要观察其如何配置扫描 Bean 注册即可,观察核心方法重写:
  1. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  2. ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  3. @Override
  4. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  5. if (!isEnabled(annotationMetadata)) {
  6. return NO_IMPORTS;
  7. }
  8. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  9. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  10. }
  11. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  12. if (!isEnabled(annotationMetadata)) {
  13. return EMPTY_ENTRY;
  14. }
  15. // 通过指定的 Annotation 获取对应属性的内容
  16. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  17. // 根据扫描得到的属性来进行配置类的加载
  18. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  19. // 剔除掉所有重复的配置类的信心
  20. configurations = removeDuplicates(configurations);
  21. // 获取所有排序的配置
  22. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  23. // 检查所有排除配置
  24. checkExcludedClasses(configurations, exclusions);
  25. // 移除排除项
  26. configurations.removeAll(exclusions);
  27. configurations = getConfigurationClassFilter().filter(configurations);
  28. fireAutoConfigurationImportEvents(configurations, exclusions);
  29. return new AutoConfigurationEntry(configurations, exclusions);
  30. }
  31. // 其他略
  32. }
  • 最终所有的扫描配置实际上都是通过这个类实现管理的,即开发者只需要在启动类中配置 @SpringBootApplication 注解会由具体的 Selector 来负责排除以及扫描包的定义。

2.2 SpringApplication 构造方法

2.2.1 概述

  • 在 SpringBoot 里面除了注解之外,随后最为重要的部分就是 SpringBoot 的启动方法:
  1. SpringApplication.run(Application.class, args);
  • 此时就需要打开这个方法来观察,此时是通过 SpringApplication 类中提供的静态方法 run() 方法来完成的:
  1. public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  2. return run(new Class<?>[] { primarySource }, args);
  3. }
  1. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  2. return new SpringApplication(primarySources).run(args);
  3. }
  • SpringApplication 类的对象实例是由 run 方法来完成实例化的处理,对于开发者来讲首先要关注的就是这个类的构造方法的具体定义了。

2.2.2 SpringApplication 类的构造方法

  1. // primarySources = Application ,就是启动类
  2. public SpringApplication(Class<?>... primarySources) {
  3. this(null, primarySources);
  4. }
  1. // primarySources = Application ,就是启动类
  2. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  3. this.resourceLoader = resourceLoader; // 获取资源加载器:加载 classpath 、文件、网络资源
  4. Assert.notNull(primarySources, "PrimarySources must not be null"); // 断言检查
  5. // 将传递过来的程序启动类的 Class 对象按照顺序保存在一个集合之中
  6. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  7. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  8. this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
  9. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  10. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  11. this.mainApplicationClass = deduceMainApplicationClass();
  12. }
  1. private ResourceLoader resourceLoader; // ResourceLoader 接口实例
  2. private Set<Class<?>> primarySources; // 所有的主要类加载源
  3. private WebApplicationType webApplicationType; // WEB 应用类型
  4. private List<Bootstrapper> bootstrappers; // 所有的类加载器
  5. private Class<?> mainApplicationClass; // 获取程序的主类

2.2.3 分析程序的应用主类

  1. private Class<?> deduceMainApplicationClass() { // 分析应用主类
  2. try {
  3. StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
  4. for (StackTraceElement stackTraceElement : stackTrace) {
  5. if ("main".equals(stackTraceElement.getMethodName())) { // 程序的启动方法
  6. return Class.forName(stackTraceElement.getClassName());
  7. }
  8. }
  9. }
  10. catch (ClassNotFoundException ex) {
  11. // Swallow and continue
  12. }
  13. return null;
  14. }
  • 根据整个类的结构来对程序的结构进行分析,如果发现有主方法(main 方法)存在,则直接返回该类的 Class 对象,最终获取到了程序启动的应用主类。

2.2.4 WebApplicationType

  • SpringBoot 应用开发可以有两种主要的处理形式:一种是基于传统的 WebServlet 编程实现的,另外一种是基于 Reactive 编程实现的。为了可以区分出不同的运行模式,就创建了一个 WebApplicationType 的枚举类型,其源代码如下:
  1. public enum WebApplicationType {
  2. NONE, // 没有运行的容器,程序没有执行,这点一般不考虑
  3. SERVLET, // 采用传统的 WebServlet 模式运行
  4. REACTIVE; // 采用响应式编程的模式运行
  5. private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
  6. "org.springframework.web.context.ConfigurableWebApplicationContext" };
  7. private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; // SpringMVC 启动类
  8. private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler"; // Reactor 启动类
  9. private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
  10. private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
  11. private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
  12. static WebApplicationType deduceFromClasspath() {
  13. if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
  14. && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
  15. return WebApplicationType.REACTIVE; // 采用 Reactive 模式运行
  16. }
  17. for (String className : SERVLET_INDICATOR_CLASSES) {
  18. if (!ClassUtils.isPresent(className, null)) {
  19. return WebApplicationType.NONE;
  20. }
  21. }
  22. return WebApplicationType.SERVLET; // 采用 Servlet 模式运行
  23. }
  24. // 其他略
  25. }
  • 这个类会通过一系列的而处理方法进行各种逻辑的判断,来推断出程序是以 Servlet 模式还是以 Reactive 模式运行。
  • 在 SpringApplication 构造方法里面有一个最为重要的方法:getSpringFactoriesInstances(Bootstrapper.class),其主要功能是进行所有的自动配置的加载(META-INF/spring.factories)。

2.2.5 getSpringFactoriesInstances

  1. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
  2. return getSpringFactoriesInstances(type, new Class<?>[] {});
  3. }
  1. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
  2. // 获取类加载器
  3. ClassLoader classLoader = getClassLoader();
  4. // 自动配置类的加载
  5. Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  6. List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  7. AnnotationAwareOrderComparator.sort(instances);
  8. return instances;
  9. }

2.2.6 loadFactoryNames

  • 对 META-INF/spring.factories 中的自动配置类读取。
  1. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  2. ClassLoader classLoaderToUse = classLoader;
  3. if (classLoaderToUse == null) {
  4. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  5. }
  6. String factoryTypeName = factoryType.getName();
  7. return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
  8. }
  1. private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  2. Map<String, List<String>> result = cache.get(classLoader);
  3. if (result != null) {
  4. return result;
  5. }
  6. result = new HashMap<>();
  7. try {
  8. Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
  9. while (urls.hasMoreElements()) {
  10. URL url = urls.nextElement();
  11. UrlResource resource = new UrlResource(url);
  12. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  13. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  14. String factoryTypeName = ((String) entry.getKey()).trim();
  15. String[] factoryImplementationNames =
  16. StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
  17. for (String factoryImplementationName : factoryImplementationNames) {
  18. result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
  19. .add(factoryImplementationName.trim());
  20. }
  21. }
  22. }
  23. // Replace all lists with unmodifiable lists containing unique elements
  24. result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
  25. .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
  26. cache.put(classLoader, result);
  27. }
  28. catch (IOException ex) {
  29. throw new IllegalArgumentException("Unable to load factories from location [" +
  30. FACTORIES_RESOURCE_LOCATION + "]", ex);
  31. }
  32. return result;
  33. }

2.2.7 getSpringFactoriesInstances

  • 将 META-INF/spring.factories 中的自动配置类注册到 Spring 容器中。
  1. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
  2. return getSpringFactoriesInstances(type, new Class<?>[] {});
  3. }
  1. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
  2. ClassLoader classLoader = getClassLoader();
  3. // Use names and ensure unique to protect against duplicates
  4. Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  5. List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  6. AnnotationAwareOrderComparator.sort(instances);
  7. return instances;
  8. }
  1. private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
  2. ClassLoader classLoader, Object[] args, Set<String> names) {
  3. List<T> instances = new ArrayList<>(names.size());
  4. for (String name : names) {
  5. try {
  6. Class<?> instanceClass = ClassUtils.forName(name, classLoader);
  7. Assert.isAssignable(type, instanceClass);
  8. Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
  9. T instance = (T) BeanUtils.instantiateClass(constructor, args);
  10. instances.add(instance);
  11. }
  12. catch (Throwable ex) {
  13. throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
  14. }
  15. }
  16. return instances;
  17. }

2.2.8 总结

  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  2. this.resourceLoader = resourceLoader;
  3. Assert.notNull(primarySources, "PrimarySources must not be null");
  4. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  5. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  6. this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
  7. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  8. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  9. this.mainApplicationClass = deduceMainApplicationClass();
  10. }
  • 通过以上的分析就可以得到以结论:SpringApplication 类中的构造方法主要完成了如下的几件事情:
  • ① 获取 ResourceLoader 接口实例,获取此接口实例的目的是为了便于类的加载操作。
  • ② 获取所有主源类,只要一个是主类,还需要对主类的结构进行分析。
  • ③ 会自动分析当前的运行模式是 Servlet 还是 ReactIve 。
  • ⑤ 获取所有的类加载器(包括 AutoConfiguration + Starter 配置的类加载器)。
  • ⑥ 初始化所有的 Bean 对象并且自动进行 Bean 注册。
  • ⑦ 设置所有的监听操作,包括事件监听等。

2.3 SpringApplication.run() 方法

2.3.1 概述

  • 在整个 SpringBoot 类名除了使用 SpringApplication 进行构造方法的初始化之外,最重要的一项就是 run() 方法,这个方法可以实现所有的相关核心处理,于是开始分析 run() 方法。
  1. SpringApplication.run(Application.class, args);
  1. public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  2. return run(new Class<?>[] { primarySource }, args);
  3. }
  1. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  2. return new SpringApplication(primarySources).run(args);
  3. }

2.3.2 run() 方法

  1. public ConfigurableApplicationContext run(String... args) {
  2. StopWatch stopWatch = new StopWatch(); // ① 实现任务的启动耗时统计
  3. stopWatch.start(); // 任务启动时执行
  4. DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // ② 创建类加载上下文
  5. ConfigurableApplicationContext context = null;
  6. configureHeadlessProperty(); // ③ 属性配置
  7. SpringApplicationRunListeners listeners = getRunListeners(args); // ④ 获取全部的监听器
  8. listeners.starting(bootstrapContext, this.mainApplicationClass);// ⑤ 启动全部监听器
  9. try {
  10. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  11. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // ⑥ Profile 环境配置
  12. configureIgnoreBeanInfo(environment);
  13. Banner printedBanner = printBanner(environment); // ⑦ 打印 Banner 信息
  14. context = createApplicationContext(); // ⑧ 创建应用上下文
  15. context.setApplicationStartup(this.applicationStartup); // ⑨ 启动上下文
  16. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 容器启动前置处理
  17. refreshContext(context); // ⑩ 刷新上下文
  18. afterRefresh(context, applicationArguments); // 容器启动后置处理
  19. stopWatch.stop(); // 任务停止计时
  20. if (this.logStartupInfo) {
  21. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  22. }
  23. listeners.started(context);
  24. callRunners(context, applicationArguments);
  25. }
  26. catch (Throwable ex) {
  27. handleRunFailure(context, ex, listeners);
  28. throw new IllegalStateException(ex);
  29. }
  30. try {
  31. listeners.running(context);
  32. }
  33. catch (Throwable ex) {
  34. handleRunFailure(context, ex, null);
  35. throw new IllegalStateException(ex);
  36. }
  37. return context;
  38. }
  • ③ 属性配置:
  1. private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
  2. private void configureHeadlessProperty() {
  3. System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
  4. System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
  5. }
  • ④ 获取全部的监听器:
  1. private SpringApplicationRunListeners getRunListeners(String[] args) {
  2. Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  3. return new SpringApplicationRunListeners(logger,
  4. getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
  5. this.applicationStartup);
  6. }
  • ⑤ 启动全部监听器:
  1. void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
  2. doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
  3. (step) -> {
  4. if (mainApplicationClass != null) {
  5. step.tag("mainApplicationClass", mainApplicationClass.getName());
  6. }
  7. });
  8. }
  • ⑥ Profile 环境配置:
  1. private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
  2. DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
  3. // Create and configure the environment
  4. ConfigurableEnvironment environment = getOrCreateEnvironment();
  5. configureEnvironment(environment, applicationArguments.getSourceArgs());
  6. ConfigurationPropertySources.attach(environment);
  7. listeners.environmentPrepared(bootstrapContext, environment);
  8. DefaultPropertiesPropertySource.moveToEnd(environment);
  9. configureAdditionalProfiles(environment);
  10. bindToSpringApplication(environment);
  11. if (!this.isCustomEnvironment) {
  12. environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
  13. deduceEnvironmentClass());
  14. }
  15. ConfigurationPropertySources.attach(environment);
  16. return environment;
  17. }
  • ⑧ 创建应用上下文:
  1. protected ConfigurableApplicationContext createApplicationContext() {
  2. return this.applicationContextFactory.create(this.webApplicationType);
  3. }
  • 总结:通过以上的程序分析可以发现,所有在 SpringBoot 之中的容器的启动都是通过 run() 方法实现的,而 run() 方法在实现处理的时候一定会自动启动 Spring 容器。

2.4 启动内置 WEB 容器

2.4.1 概述

  • SpringBoot 应用一旦启动之后,就会启动内置的 WEB 服务器,如:Tomcat、Jetty、Undertow,容器的启动实际上都是由 run() 方法完成的。
  1. public ConfigurableApplicationContext run(String... args) {
  2. StopWatch stopWatch = new StopWatch();
  3. stopWatch.start();
  4. DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  5. ConfigurableApplicationContext context = null;
  6. configureHeadlessProperty();
  7. SpringApplicationRunListeners listeners = getRunListeners(args);
  8. listeners.starting(bootstrapContext, this.mainApplicationClass);
  9. try {
  10. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  11. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
  12. configureIgnoreBeanInfo(environment);
  13. Banner printedBanner = printBanner(environment);
  14. // 分析重点
  15. context = createApplicationContext();
  16. // 分析重点
  17. context.setApplicationStartup(this.applicationStartup);
  18. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
  19. refreshContext(context);
  20. afterRefresh(context, applicationArguments);
  21. stopWatch.stop();
  22. if (this.logStartupInfo) {
  23. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  24. }
  25. listeners.started(context);
  26. callRunners(context, applicationArguments);
  27. }
  28. catch (Throwable ex) {
  29. handleRunFailure(context, ex, listeners);
  30. throw new IllegalStateException(ex);
  31. }
  32. try {
  33. listeners.running(context);
  34. }
  35. catch (Throwable ex) {
  36. handleRunFailure(context, ex, null);
  37. throw new IllegalStateException(ex);
  38. }
  39. return context;
  40. }
  • 如果要想启动内置的 WEB 容器,肯定是依靠 createApplicationContext() 方法完成的,本次观察这个方法的具体实现。
  1. private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
  2. protected ConfigurableApplicationContext createApplicationContext() {
  3. return this.applicationContextFactory.create(this.webApplicationType);
  4. }

2.4.2 ApplicationContextFactory

  • 容器的启动主要依据的是 ApplicationContextFactory 的对象实例,其源代码如下:
  1. package org.springframework.boot;
  2. import java.util.function.Supplier;
  3. import org.springframework.beans.BeanUtils;
  4. import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
  5. import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
  6. import org.springframework.context.ConfigurableApplicationContext;
  7. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  8. @FunctionalInterface
  9. public interface ApplicationContextFactory {
  10. ApplicationContextFactory DEFAULT = (webApplicationType) -> {
  11. try {
  12. switch (webApplicationType) { // 判断 WEB 应用类型
  13. case SERVLET: // 普通的 Servlet 模式
  14. return new AnnotationConfigServletWebServerApplicationContext();
  15. case REACTIVE: // 响应式 Reactive 模式
  16. return new AnnotationConfigReactiveWebServerApplicationContext();
  17. default:
  18. return new AnnotationConfigApplicationContext();
  19. }
  20. }
  21. catch (Exception ex) {
  22. throw new IllegalStateException("Unable create a default ApplicationContext instance, "
  23. + "you may need a custom ApplicationContextFactory", ex);
  24. }
  25. };
  26. ConfigurableApplicationContext create(WebApplicationType webApplicationType);
  27. static ApplicationContextFactory ofContextClass(Class<? extends ConfigurableApplicationContext> contextClass) {
  28. return of(() -> BeanUtils.instantiateClass(contextClass));
  29. }
  30. static ApplicationContextFactory of(Supplier<ConfigurableApplicationContext> supplier) {
  31. return (webApplicationType) -> supplier.get();
  32. }
  33. }
  • 在使用 DEFAULT 内置的实例化对象创建应用程序的时候,会判断当前的应用类型是 SERVLET 还是 REACTIVE,本次就针对于 SERVLET 进行分析。

2.4.3 AnnotationConfigServletWebServerApplicationContext

  1. package org.springframework.boot.web.servlet.context;
  2. import java.util.Arrays;
  3. import java.util.LinkedHashSet;
  4. import java.util.Set;
  5. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
  6. import org.springframework.beans.factory.support.BeanNameGenerator;
  7. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
  8. import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
  9. import org.springframework.context.annotation.AnnotationConfigRegistry;
  10. import org.springframework.context.annotation.AnnotationConfigUtils;
  11. import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
  12. import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
  13. import org.springframework.context.annotation.ScopeMetadataResolver;
  14. import org.springframework.core.env.ConfigurableEnvironment;
  15. import org.springframework.stereotype.Component;
  16. import org.springframework.util.Assert;
  17. import org.springframework.util.ClassUtils;
  18. public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
  19. implements AnnotationConfigRegistry {
  20. private final AnnotatedBeanDefinitionReader reader;
  21. private final ClassPathBeanDefinitionScanner scanner;
  22. private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();
  23. private String[] basePackages;
  24. public AnnotationConfigServletWebServerApplicationContext() { // ① 无参构造方法
  25. this.reader = new AnnotatedBeanDefinitionReader(this); // 读取注解配置的 Bean
  26. this.scanner = new ClassPathBeanDefinitionScanner(this); // 读取 Classpath 扫描的 Bean
  27. }
  28. public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
  29. super(beanFactory);
  30. this.reader = new AnnotatedBeanDefinitionReader(this);
  31. this.scanner = new ClassPathBeanDefinitionScanner(this);
  32. }
  33. /**
  34. */
  35. public AnnotationConfigServletWebServerApplicationContext(Class<?>... annotatedClasses) {
  36. this();
  37. register(annotatedClasses);
  38. refresh();
  39. }
  40. /**
  41. */
  42. public AnnotationConfigServletWebServerApplicationContext(String... basePackages) {
  43. this();
  44. scan(basePackages);
  45. refresh();
  46. }
  47. /**
  48. * Profile 配置项
  49. */
  50. @Override
  51. public void setEnvironment(ConfigurableEnvironment environment) {
  52. super.setEnvironment(environment);
  53. this.reader.setEnvironment(environment);
  54. this.scanner.setEnvironment(environment);
  55. }
  56. public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { // Bean 的配置
  57. this.reader.setBeanNameGenerator(beanNameGenerator);
  58. this.scanner.setBeanNameGenerator(beanNameGenerator);
  59. getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
  60. }
  61. public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
  62. this.reader.setScopeMetadataResolver(scopeMetadataResolver);
  63. this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
  64. }
  65. @Override
  66. public final void register(Class<?>... annotatedClasses) { // 注册管理
  67. Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
  68. this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));
  69. }
  70. @Override
  71. public final void scan(String... basePackages) { // 扫描处理
  72. Assert.notEmpty(basePackages, "At least one base package must be specified");
  73. this.basePackages = basePackages;
  74. }
  75. @Override
  76. protected void prepareRefresh() { // 刷新处理
  77. this.scanner.clearCache();
  78. super.prepareRefresh();
  79. }
  80. @Override
  81. protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  82. super.postProcessBeanFactory(beanFactory);
  83. if (this.basePackages != null && this.basePackages.length > 0) {
  84. this.scanner.scan(this.basePackages);
  85. }
  86. if (!this.annotatedClasses.isEmpty()) {
  87. this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
  88. }
  89. }
  90. }
  • 所有启动上下文环境初始化完成之后,随后就需要进行最终的启动处理了,而在 run() 方法里面通过以下方法 context.setApplicationStartup(this.applicationStartup); 实现应用的启动。