SpringBoot 原理深入及源码剖析

发布时间:2024年01月12日

SpringBoot 依赖管理

SpringBoot 项目间接继承 spring-boot-dependencies,该文件对常用技术框架进行了统一版本管理,所以在SpringBoot 项目 pom.xml 引入spring-boot-dependencies管理的依赖文件不需要标注依赖文件版本号。引入 starter 就可以实现对应场景开发,而不需要额外导入相关依赖文件。

自动配置(启动流程)

SpringBoot 应用启动入口是@SpringBootApplication?注解标注类中的?main()?方法,@SpringBootApplication?能够扫描 Spring 组件并自动配置 SpringBoot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// 标明该类为配置类
@SpringBootConfiguration
// 启动自动配置功能
@EnableAutoConfiguration
// 包扫描器
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),


@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

1. @SpringBootConfiguration 注解

@SpringBootConfiguration注解表示 Spring Boot 配置类,该注解仅仅是对@Configuration?注解的简单封装,与@Configuration?注解作用相同。

2. @EnableAutoConfiguration 注解

@EnableAutoConfiguration?注解表示开启自动配置功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包
@AutoConfigurationPackage
// 自动配置类扫描导入
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};
? ? String[] excludeName() default {};
}

  • @AutoConfigurationPackage?注解

    1
    2
    3
    4
    5
    6
    7
    8

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    // 导入 Registrar 组件类
    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
    }

    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

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    // 这个方法是导入组件类的具体实现
    ? ? @Override
    ? ? public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    ? ? ? ? // 将主程序类所在包及其子包下的组件扫描到 Spring 容器中
    ? ? ? ? register(registry, new PackageImport(metadata).getPackageName());
    ? ? }

    ? ? @Override
    ? ? public Set<Object> determineImports(AnnotationMetadata metadata) {
    ? ? ? ? return Collections.singleton(new PackageImport(metadata));
    ? ? }
    }

    private static final class PackageImport {
    ? ? private final String packageName;
    ? ? PackageImport(AnnotationMetadata metadata) {
    ? ? ? ? // 获取注解包名
    ? ? ? ? this.packageName = ClassUtils.getPackageName(metadata.getClassName());
    ? ? }
    ? ? String getPackageName() {
    ? ? ? ? return this.packageName;

    }
    }

  • @Import(AutoConfigurationImportSelector.class)

    将?AutoConfigurationImportSelector?这个类导入到 Spring 容器中,?AutoConfigurationImportSelector?可以帮助 SpringBoot 应用将所有符合条件的配置都加载到当前 SpringBoot 创建并使用的 IoC 容器(ApplicationContext) 中。

    通过源码分析这个类中是通过?selectImports()?这个方法告诉 SpringBoot 都需要导入哪些组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    // 这个方法告诉springboot都需要导入那些组件
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    ? ? //判断 enableautoconfiguration注解有没有开启,默认开启(是否进行自动装配)
    ? ? if (!isEnabled(annotationMetadata)) {
    ? ? ? ? return NO_IMPORTS;
    ? ? }
    ? ? //1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件
    ? ? //作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。
    ? ? // SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类
    ? ? // 自动配置的类全名.条件=值
    ? ? AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    ? ?
    ? ? AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
    ? ? return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

    • loadMetadata()

      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
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198

      final class AutoConfigurationMetadataLoader {
      ? ? //文件中为需要加载的配置类的类路径
      ? ? protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";


      private AutoConfigurationMetadataLoader() {

      }
      ? ? public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
      ? ? ? ? //重载方法
      ? ? ? ? return loadMetadata(classLoader, PATH);

      }
      ? ?
      ? ? static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
      ? ? ? ? try {
      ? ? ? ? ? ? //1.读取spring-boot-autoconfigure.jar包中spring-autoconfigure-metadata.properties的信息生成urls枚举对象
      ? ? ? ? ? ? Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
      ? ? ? ? ? ? // 遍历 URL 数组,读取到 properties 中
      ? ? ? ? ? ? Properties properties = new Properties();

      ? ? ? ? ? ? //2.解析urls枚举对象中的信息封装成properties对象并加载
      ? ? ? ? ? ? while (urls.hasMoreElements()) {
      ? ? ? ? ? ? ? ? properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
      ? ? ? ? ? ? }
      ? ? ? ? ? ?



      // 将 properties 转换成 PropertiesAutoConfigurationMetadata 对象



      //根据封装好的properties对象生成AutoConfigurationMetadata对象返回



      return loadMetadata(properties);


      } catch (IOException ex) {



      throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);


      }

      }


      static AutoConfigurationMetadata loadMetadata(Properties properties) {


      return new PropertiesAutoConfigurationMetadata(properties);

      }


      /**

      ?* {@link AutoConfigurationMetadata} implementation backed by a properties file.

      ?*/

      private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {

      ? ? ? ? /**
      ? ? ? ? ?* Properties 对象
      ? ? ? ? ?*/


      private final Properties properties;



      PropertiesAutoConfigurationMetadata(Properties properties) {



      this.properties = properties;


      }



      @Override


      public boolean wasProcessed(String className) {



      return this.properties.containsKey(className);


      }



      @Override


      public Integer getInteger(String className, String key) {



      return getInteger(className, key, null);


      }



      @Override


      public Integer getInteger(String className, String key, Integer defaultValue) {



      String value = get(className, key);



      return (value != null) ? Integer.valueOf(value) : defaultValue;


      }



      @Override


      public Set<String> getSet(String className, String key) {



      return getSet(className, key, null);


      }



      @Override


      public Set<String> getSet(String className, String key, Set<String> defaultValue) {



      String value = get(className, key);



      return (value != null) ? StringUtils.commaDelimitedListToSet(value) : defaultValue;


      }



      @Override


      public String get(String className, String key) {



      return get(className, key, null);


      }



      @Override


      public String get(String className, String key, String defaultValue) {



      String value = this.properties.getProperty(className + "." + key);



      return (value != null) ? value : defaultValue;


      }

      }
      }

    • getAutoConfigurationEntry()

      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
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55

      protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
      ? ? // 1. 判断是否开启注解。如未开启,返回空串
      ? ? if (!isEnabled(annotationMetadata)) {
      ? ? ? ? return EMPTY_ENTRY;
      ? ? }
      ? ? // 2. 获得注解的属性
      ? ? AnnotationAttributes attributes = getAttributes(annotationMetadata);

      ? ? // 3. getCandidateConfigurations()用来获取默认支持的自动配置类名列表
      ? ? // spring Boot在启动的时候,使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,
      ? ? // 找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性定义的工厂类名称,
      ? ? // 将这些值作为自动配置类导入到容器中,自动配置类就生效了
      ? ? List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);


      ? ? // 3.1 //去除重复的配置类,若我们自己写的starter 可能存在重复的
      ? ? configurations = removeDuplicates(configurations);
      ? ? // 4. 如果项目中某些自动配置类,我们不希望其自动配置,我们可以通过EnableAutoConfiguration的exclude或excludeName属性进行配置,
      ? ? // 或者也可以在配置文件里通过配置项“spring.autoconfigure.exclude”进行配置。
      ? ? //找到不希望自动配置的配置类(根据EnableAutoConfiguration注解的一个exclusions属性)
      ? ? Set<String> exclusions = getExclusions(annotationMetadata, attributes);
      ? ? // 4.1 校验排除类(exclusions指定的类必须是自动配置类,否则抛出异常)
      ? ? checkExcludedClasses(configurations, exclusions);
      ? ? // 4.2 从 configurations 中,移除所有不希望自动配置的配置类
      ? ? configurations.removeAll(exclusions);

      ? ? // 5. 对所有候选的自动配置类进行筛选,根据项目pom.xml文件中加入的依赖文件筛选出最终符合当前项目运行环境对应的自动配置类

      ? ? //@ConditionalOnClass : 某个class位于类路径上,才会实例化这个Bean。
      ? ? //@ConditionalOnMissingClass : classpath中不存在该类时起效
      ? ? //@ConditionalOnBean : DI容器中存在该类型Bean时起效
      ? ? //@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
      ? ? //@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
      ? ? //@ConditionalOnExpression : SpEL表达式结果为true时
      ? ? //@ConditionalOnProperty : 参数设置或者值一致时起效
      ? ? //@ConditionalOnResource : 指定的文件存在时起效
      ? ? //@ConditionalOnJndi : 指定的JNDI存在时起效
      ? ? //@ConditionalOnJava : 指定的Java版本存在时起效
      ? ? //@ConditionalOnWebApplication : Web应用环境下起效
      ? ? //@ConditionalOnNotWebApplication : 非Web应用环境下起效

      ? ? //总结一下判断是否要加载某个类的两种方式:
      ? ? //根据spring-autoconfigure-metadata.properties进行判断。
      ? ? //要判断@Conditional是否满足
      ? ? // 如@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })表示需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类才能完成自动注册。
      ? ? configurations = filter(configurations, autoConfigurationMetadata);


      ? ? // 6. 将自动配置导入事件通知监听器
      ? ? //当AutoConfigurationImportSelector过滤完成后会自动加载类路径下Jar包中META-INF/spring.factories文件中 AutoConfigurationImportListener的实现类,
      ? ? // 并触发fireAutoConfigurationImportEvents事件。
      ? ? fireAutoConfigurationImportEvents(configurations, exclusions);
      ? ? // 7. 创建 AutoConfigurationEntry 对象
      ? ? return new AutoConfigurationEntry(configurations, exclusions);
      }

      • getCandidateConfigurations()

        1
        2
        3
        4
        5
        6
        7

        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        ? ? // 让SpringFactoryLoader去加载一些组件的名字
        ? ? List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        ? ? // 断言,非空
        ? ? Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct.");
        ? ? return configurations;
        }

        • loadFactoryNames()

          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
          39
          40
          41
          42
          43
          44
          45
          46
          47
          48
          49
          50
          51
          52
          53
          54
          55
          56
          57
          58
          59
          60
          61
          62
          63
          64
          65
          66
          67
          68
          69
          70
          71
          72

          public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
          ? ? String factoryClassName = factoryClass.getName();
          ? ? return loadSpringFactories(classLoader).getOrDefault( factoryClassName, Collections.emptyList());
          }


          private static Map<String, List<String>> loadSpringFactories( @Nullable ClassLoader classLoader) {
          ? ? ? ? MultiValueMap<String, String> result = cache.get(classLoader);
          ? ? ? ? if (result != null) {
          ? ? ? ? ? ? return result;
          ? ? ? ? }
          ? ? ? ?
          ? ? ? ? try {
          ? ? ? ? ? ? // 如果类加载器不为 null,则加载类路径下spring.factories,将其中设置的配置类的全路径信息封装为 Enumeration 类对象
          ? ? ? ? ? ? // public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
          ? ? ? ? ? ? Enumeration<URL> urls = (classLoader != null ?
          classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
          ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));



          result = new LinkedMultiValueMap<>();
          ? ? ? ? ? ? // 循环 Enumeration 类对象,根据相应的节点信息生成 Properties 对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为 Array,方法result集合中



          while (urls.hasMoreElements()) {
          ? ? ? ? ? ? ? ? URL url = urls.nextElement();
          ? ? ? ? ? ? ? ? UrlResource resource = new UrlResource(url);
          ? ? ? ? ? ? ? ? Properties properties = PropertiesLoaderUtils.loadProperties(resource);
          ? ? ? ? ? ? ? ? for (Map.Entry<?, ?> entry : properties.entrySet()) {
          ? ? ? ? ? ? ? ? ? ? String factoryClassName = ((String) entry.getKey()).trim();
          ? ? ? ? ? ? ? ? ? ? for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
          ? ? ? ? ? ? ? ? ? ? ? ? result.add(factoryClassName, factoryName.trim());





          }




          }



          }



          cache.put(classLoader, result);



          return result;


          }


          catch (IOException ex) {



          throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);


          }

          }

    @EnableAutoConfiguration?就是从classpath 中搜寻?MATE-INF/spring.factories?配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration?对应的配置项通过反射示例化为对应的标注了@Configuration?的JavaConfig形式的配置类,并加载到 IoC 容器中。

总结:

Springboot 底层实现自动装配的步骤是:

  1. Springboot 应用启动;
  2. @SpringBootApplication?起作用;
  3. @EnableAutoConfiguration;
  4. @AutoConfigurationPackage:?这个组合注解主要是?@Import(AutoConfigurationPackages.Registrar.class), 它通过将?Registrar?类导入到容器中,而?Registrar?类作用是扫描主配置类同级目录及其子包,并将相应的组件导入到 Springboot创建管理容器中
  5. @Import(AutoConfigurationImportSelector.class):?它通过将?AutoConfigurationImportSelector?类导入到容器中,AutoConfigurationImportSelector?类作用是通过selectImports方法只想的过程中,会使用内部工具类SpringFactoriesLoader查找?classpath?上所用 jar 包中的?MATE-INF/spring.factories?进行加载, 实现将配置类信息交给?SpringFactory?加载器进行一系列的容器创建过程。
3. @ComponentScan 注解

@ComponentScan?注解具体扫描的包的根路径由 Spring Boot 项目主程序启动类所在包的位置决定,在扫描过程中由前面介绍的?@AutoConfigurationPackage?注解进行解析,从而得到 Springboot 项目主程序启动类所在包的具体位置。

总结:

1
2
3
4
5
6
7

|- @SpringBootConfiguration

|- @Configuration //通过javaConfig的方式来添加组件到 IoC 容器中
|- @EnableAutoConfiguration
? ? |- @AutoConfigurationPackage //自动配置包,与@ComponentScan扫描到的添加到IOC
? ? |- @Import(AutoConfigurationImportSelector.class) //到META-INF/spring.factories中定义的bean 添加到 IoC 容器中
|- @ComponentScan //包扫描

自定义Starter

Starter 使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由Springboot 自动通过classpath 路径下的类发现需要的 bean,并织入相应的 Bean。

步骤
  1. 自建工程,命名为xxx-spring-boot-starter,导入spring-boot-autoconfigure依赖

  2. 编写配置类

    • @Configuration
    • @ConditionalOnXxx?: 标明自动配置条件
  3. resources 下创建?/META-INF/spring.factories,在该文件中配置自定义配置类

    1

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\ com.test.config.MyAutoConfiguration

  4. 测试使用

SpringApplication.run()执行原理

源代码

1
2
3
4
5
6
7
8
9
10
11

//调用静态类,参数对应的就是SpringbootDemoApplication.class以及main方法中的args
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {


? ? return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
? ? //SpringApplication的启动由两部分组成:
? ? //1. 实例化SpringApplication对象
? ? //2. run(args):调用run方法
? ? return new SpringApplication(primarySources).run(args);
}
1. SpringApplication 实例的初始化创建

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 SpringApplication(Class<?>... primarySources) {
? ? this(null, primarySources);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
? ? this.sources = new LinkedHashSet();
? ? // Banner 模式
? ? this.bannerMode = Mode.CONSOLE;
? ? this.logStartupInfo = true;
? ? // 是否添加 JVM 启动参数
? ? this.addCommandLineProperties = true;
? ? this.addConversionService = true;
? ? this.headless = true;
? ? this.registerShutdownHook = true;
? ? this.additionalProfiles = new HashSet();
? ? this.isCustomEnvironment = false;
? ? // 资源加载器
? ? this.resourceLoader = resourceLoader;
? ? Assert.notNull(primarySources, "PrimarySources must not be null");

? ? //项目启动类 SpringbootDemoApplication.class设置为属性存储起来
? ? this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

? ? //设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)


? ? // deduceFromClasspath()方法用于查看 ClassPath类路径下是否存在某个特征类,从而判断webApplicationType
? ? this.webApplicationType = WebApplicationType.deduceFromClasspath();

? ? // 设置初始化器(Initializer),最后会调用这些初始化器
? ? //所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
? ? setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

? ? // 设置监听器(Listener)
? ? setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

? ? // 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
? ? this.mainApplicationClass = deduceMainApplicationClass();
}
2. 项目初始化启动

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

public ConfigurableApplicationContext run(String... args) {
? ? // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
? ? StopWatch stopWatch = new StopWatch();
? ? stopWatch.start();
? ? // 初始化应用上下文和异常报告集合
? ? ConfigurableApplicationContext context = null;
? ? Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
? ? // 配置 headless 属性
? ? configureHeadlessProperty();

? ? //(1)获取并启动监听器
? ? SpringApplicationRunListeners listeners = getRunListeners(args);
? ? listeners.starting();
? ? try {
? ? ? ? // 创建 ?ApplicationArguments 对象 初始化默认应用参数类
? ? ? ? // args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
? ? ? ? ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

? ? ? ? //(2)项目运行环境Environment的预配置
? ? ? ? // 创建并配置当前SpringBoot应用将要使用的Environment
? ? ? ? // 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
? ? ? ? ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
? ? ? ? // 排除不需要的运行环境
? ? ? ? configureIgnoreBeanInfo(environment);
? ? ? ? // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
? ? ? ? Banner printedBanner = printBanner(environment);

? ? ? ? // (3)创建Spring容器
? ? ? ? // 根据 webApplicationType 进行判断,确定容器类型,如果为 SERVLET 类型,会反射创建相应的字节码,AnnotationConfigServletWebServerApplicationContext, 接着使用之前传世话设置的context、environment、listeners、applicationArgument 进行应用上下文的组装配置
? ? ? ? context = createApplicationContext();
? ? ? ? // 获得异常报告器 SpringBootExceptionReporter 数组
? ? ? ? //这一步的逻辑和实例化初始化器和监听器的一样,
? ? ? ? // 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
? ? ? ? exceptionReporters = getSpringFactoriesInstances(
? ? ? ? ? ? SpringBootExceptionReporter.class,
? ? ? ? ? ? new Class[] { ConfigurableApplicationContext.class }, context);


? ? ? ? // (4)Spring容器前置处理
? ? ? ? //这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
? ? ? ? prepareContext(context, environment, listeners, applicationArguments,
? ? ? ? ? ? ? ? ? ? ? ?printedBanner);

? ? ? ? // (5):刷新容器
? ? ? ? // 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等),同时向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
? ? ? ? refreshContext(context);

? ? ? ? // (6):Spring容器后置处理
? ? ? ? //扩展接口,设计模式中的模板方法,默认为空实现。
? ? ? ? // 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
? ? ? ? afterRefresh(context, applicationArguments);
? ? ? ? // 停止 StopWatch 统计时长
? ? ? ? stopWatch.stop();
? ? ? ? // 打印 Spring Boot 启动的时长日志。
? ? ? ? if (this.logStartupInfo) {
? ? ? ? ? ? new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
? ? ? ? }
? ? ? ? // (7)发出结束执行的事件通知
? ? ? ? listeners.started(context);

? ? ? ? // (8):执行Runners
? ? ? ? //用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
? ? ? ? //Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
? ? ? ? //Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
? ? ? ? callRunners(context, applicationArguments);
? ? } catch (Throwable ex) {
? ? ? ? // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
? ? ? ? handleRunFailure(context, ex, exceptionReporters, listeners);
? ? ? ? throw new IllegalStateException(ex);
? ? }

? ? // ? (9)发布应用上下文就绪事件
? ? //表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
? ? // 这样整个Spring Boot项目就正式启动完成了。
? ? try {
? ? ? ? listeners.running(context);
? ? } catch (Throwable ex) {
? ? ? ? // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
? ? ? ? handleRunFailure(context, ex, exceptionReporters, null);
? ? ? ? throw new IllegalStateException(ex);
? ? }
? ? //返回容器
? ? return context;
}

文章来源:https://blog.csdn.net/zhangjunli/article/details/135554325
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。