ToB企服应用市场:ToB评测及商务社交产业平台

标题: SpringBoot启动代码和自动装配源码分析 [打印本页]

作者: 杀鸡焉用牛刀    时间: 2022-8-22 05:14
标题: SpringBoot启动代码和自动装配源码分析
​                随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多。每一种组件与Spring容器整合需要实现相关代码。SpringMVC框架配置由于太过于繁琐和依赖XML文件;为了方便快速集成第三方组件和减少对配置文件的依赖,SpringBoot应运而生,其中采用了约定大于配置的理论让开发者不需要过多配置即可进行开发。SpringBoot底层使用的Spring ,默认集成了N多组件的自动装配。使用SpringBoot很简单,在主类中添加一个@SpringBootApplication,以及调用SpringApplication.run()并传入主类。代码如下
  1. @SpringBootApplication
  2. public class StartApp {
  3.     public static void main(String[] args) {
  4.         SpringApplication.run(StartApp.class);
  5.     }
  6. }
复制代码
由上面的源码可知,SpringApplication.run()是SpringBoot的程序入口。本文会从SpringApplication.run()和@SpringBootApplication注解两方面来分析。
一、SpringBoot启动代码主线分析

​                SpringApplication.run(StartApp.class)的中关键代码,先创建一个SpringApplication类,再执行run方法。代码如下,
  1. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  2.    return new SpringApplication(primarySources).run(args);
  3. }
复制代码
  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  2.     // 设置资源加载器
  3.     this.resourceLoader = resourceLoader;
  4.     Assert.notNull(primarySources, "PrimarySources must not be null");
  5.     // 设置应用主配置类
  6.     this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  7.     // 获取web服务器类型
  8.     this.webApplicationType = WebApplicationType.deduceFromClasspath();
  9.     // 从spring.factories 文件中获取 ApplicationContextInitializer 的实现类
  10.     setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  11.     // 从spring.factories 文件中获取 ApplicationListener 监听器的实现类
  12.     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  13.     // 设置main启动类
  14.     this.mainApplicationClass = deduceMainApplicationClass();
  15. }
复制代码
构造方法中主要逻辑:
​                                1.设置应用主配置类,后面的run方法中会用它封装成 BeanDefinitionHolder 并加载到 context 的 registry 中。
​                                2.获取web服务器类型,后面的run方法中会用它来创建具体的web服务类型。
​                                3.从spring.factories 文件中获取 ApplicationContextInitializer 的实现类,并设置给SpringApplication实例
​                                4.从spring.factories 文件中获取 ApplicationListener 监听器的实现类,并设置给SpringApplication实例
​                                5.设置main启动类
其中getSpringFactoriesInstances方法主要逻辑是:从META-INF/spring.factories文件中根据接口获取具体实现类字符串,并把字符串成实例化为对象。代码如下,
  1. // 获取类加载器
  2. ClassLoader classLoader = getClassLoader();
  3. // Use names and ensure unique to protect against duplicates
  4. // 根据type 从META-INF/spring.factories获取 具体的实现类字符串列表
  5. Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  6. // 实例化具体的实现类
  7. List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  8. // 排序
  9. AnnotationAwareOrderComparator.sort(instances);
  10. return instances;
复制代码
在META-INF/spring.factories文件中ApplicationContextInitializer.class 对应的实现类字符串为,
  1. org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
  2. org.springframework.boot.context.ContextIdApplicationContextInitializer,\
  3. org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
  4. org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
  5. org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
复制代码
在META-INF/spring.factories文件中ApplicationListener.class 对应的实现类字符串为,
  1. org.springframework.boot.ClearCachesApplicationListener,\
  2. org.springframework.boot.builder.ParentContextCloserApplicationListener,\
  3. org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
  4. org.springframework.boot.context.FileEncodingApplicationListener,\
  5. org.springframework.boot.context.config.AnsiOutputApplicationListener,\
  6. org.springframework.boot.context.config.ConfigFileApplicationListener,\
  7. org.springframework.boot.context.config.DelegatingApplicationListener,\
  8. org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
  9. org.springframework.boot.context.logging.LoggingApplicationListener,\
  10. org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
复制代码
  1. StopWatch stopWatch = new StopWatch();
  2. stopWatch.start();
  3. ConfigurableApplicationContext context = null;
  4. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  5. // 设置了一个名为 java.awt.headless 的系统属性
  6. // 其实是想设计应用程序,即使没有检测到显示器,也允许其启动
  7. // 对于服务器来说,是不需要显示器的 ,所以要这样设置
  8. configureHeadlessProperty();
  9. // 获取 SpringApplicationRunListener 加载的是 EventPublishingRunListener
  10. // 获取启动时的监听器
  11. SpringApplicationRunListeners listeners = getRunListeners(args);
  12. // 触发启动事件
  13. listeners.starting();
  14. try {
  15.     // 构造一个应用程序的参数持有类
  16.     ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  17.     // 创建并配置环境
  18.     ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  19.     // 配置需要忽略的BeanInfo信息
  20.     configureIgnoreBeanInfo(environment);
  21.     Banner printedBanner = printBanner(environment);
  22.     // 创建上下文对象
  23.     context = createApplicationContext();
  24.     // 加载配置的启动异常处理器
  25.     exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
  26.                                                      new Class[] { ConfigurableApplicationContext.class }, context);
  27.     // 刷新前操作
  28.     prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  29.     // 刷新应用上下文 完成 Spring 容器的初始化
  30.     refreshContext(context);
  31.     // 刷新后操作
  32.     afterRefresh(context, applicationArguments);
  33.     stopWatch.stop();
  34.     if (this.logStartupInfo) {
  35.         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  36.     }
  37.     // 启动完成事件
  38.     listeners.started(context);
  39.     // 执行 ApplicationRunner 和 CommandLineRunner 实现类
  40.     callRunners(context, applicationArguments);
  41. }
  42. catch (Throwable ex) {
  43.     // 事件广播启动出错了
  44.     handleRunFailure(context, ex, exceptionReporters, listeners);
  45.     throw new IllegalStateException(ex);
  46. }
  47. try {
  48.     // 运行事件
  49.     listeners.running(context);
  50. }
  51. catch (Throwable ex) {
  52.     handleRunFailure(context, ex, exceptionReporters, null);
  53.     throw new IllegalStateException(ex);
  54. }
  55. return context;
复制代码
run方法中主要逻辑:
​                                1. 从spring.factories 文件中获取 SpringApplicationRunListener 的实现类(监听事件发布器),并在context生命周期中执行相关的事件 ,比如触发启动事件、启动完成事件等。
​                                2.创建Web应用上下文对象,根据webApplicationType来创建具体的web服务类型。
​                                3.刷新前操作,把主配置类资源封装成 BeanDefinitionHolder 加载到 context 的 registry 中。
​                                4.刷新应用上下文 完成 Spring 容器的初始化。
​                                5.执行 实现了 ApplicationRunner 和 CommandLineRunner 接口的类。
二、SpringBoot自动装配原理分析

1.自动装配的前置知识@Import

​                @SpringBootApplication注解其中主要是利用@Import 注解,@Import源码如下:
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Import {
  5.     /**
  6.          * {@link Configuration @Configuration}, {@link ImportSelector},
  7.          * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
  8.          */
  9.     Class<?>[] value();
  10. }
复制代码
@Import在注解一般和@Configuration一起用,Spring容器初始化的过程中会进行解析@Configuration注解类(源码在org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions中),其过程会解析注解类的@Import注解的元数据,并根据类是否实现相关接口进行处理。源码位置:org.springframework.context.annotation.ConfigurationClassParser#processImports;关键代码如下,
  1. try {
  2.     for (SourceClass candidate : importCandidates) {
  3.         if (candidate.isAssignable(ImportSelector.class)) {
  4.             // Candidate class is an ImportSelector -> delegate to it to determine imports
  5.             Class<?> candidateClass = candidate.loadClass();
  6.             ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
  7.                                                                            this.environment, this.resourceLoader, this.registry);
  8.             Predicate<String> selectorFilter = selector.getExclusionFilter();
  9.             if (selectorFilter != null) {
  10.                 exclusionFilter = exclusionFilter.or(selectorFilter);
  11.             }
  12.             if (selector instanceof DeferredImportSelector) {
  13.                 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
  14.             }
  15.             else {
  16.                 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
  17.                 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
  18.                 processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
  19.             }
  20.         }
  21.         else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
  22.             // Candidate class is an ImportBeanDefinitionRegistrar ->
  23.             // delegate to it to register additional bean definitions
  24.             Class<?> candidateClass = candidate.loadClass();
  25.             ImportBeanDefinitionRegistrar registrar =
  26.                 ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
  27.                                                      this.environment, this.resourceLoader, this.registry);
  28.             configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
  29.         }
  30.         else {
  31.             // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
  32.             // process it as an @Configuration class
  33.             this.importStack.registerImport(
  34.                 currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
  35.             processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
  36.         }
  37.     }
  38. }
复制代码
从上面代码可知@Import的value类使用有三种场景:
​                1.实现了 ImportSelector.class接口的场景;直接调用实例selector 的selectImports方法返回要实例化的Bean对象的全类名列表,并根据全类名字符串列表创建实例对象,然后递归调用当前的processImports 方法,最终会添加到configurationClasses的集合中,configurationClasses集合中的对象会被注册到BeanDefinitionRegistry类型的 registry 对象中。实现接口ImportSelector这种情况下又扩展了 DeferredImportSelector 接口的情况,该接口用来实现BeanDefinition的延迟注入功能更。DeferredImportSelector接口扩展了ImportSelector接口,并且其中有个内部接口 Group,如果某个@Import注解的value类实现了DeferredImportSelector接口并且也实现了该接口的内部类Group接口,则表面此实现类需要延迟处理。如果是需要延迟处理,则会把ImportSelector 实例selector 组装成 DeferredImportSelectorHolder 对象添加到 deferredImportSelectors集合中,处理逻辑源码位置: org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle;关键代码如下,
  1. public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
  2.     DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
  3.     if (this.deferredImportSelectors == null) {
  4.         DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
  5.         handler.register(holder);
  6.         handler.processGroupImports();
  7.     }
  8.     else {
  9.         this.deferredImportSelectors.add(holder);
  10.     }
  11. }
复制代码
DeferredImportSelector接口的实现逻辑会在org.springframework.context.annotation.ConfigurationClassParser#parse方法中调用,具体代码在this.deferredImportSelectorHandler.process()中,关键代码如下,
  1. public void process() {
  2.     List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
  3.     this.deferredImportSelectors = null;
  4.     try {
  5.         if (deferredImports != null) {
  6.             DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
  7.             deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
  8.             deferredImports.forEach(handler::register);
  9.             // 具体的执行逻辑
  10.             handler.processGroupImports();
  11.         }
  12.     }
  13.     finally {
  14.         this.deferredImportSelectors = new ArrayList<>();
  15.     }
  16. }
复制代码
在processGroupImports()方法中,先通过grouping.getImports()拿到需要自动装配的Group.Entry(封装了全类名)对象集合,然后通过processImports()方法根据Entry类名字符串进行创建SourceClass类(该类可以通过asConfigClass()方法转成ConfigurationClass对象),最终添加到configurationClasses集合中。代码如下,
  1. public void processGroupImports() {
  2.     for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
  3.         Predicate<String> exclusionFilter = grouping.getCandidateFilter();
  4.         grouping.getImports().forEach(entry -> {
  5.             ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
  6.             try {
  7.                 processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
  8.                                Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
  9.                                exclusionFilter, false);
  10.             }
  11.             catch (BeanDefinitionStoreException ex) {
  12.                 throw ex;
  13.             }
  14.             catch (Throwable ex) {
  15.                 throw new BeanDefinitionStoreException(
  16.                     "Failed to process import candidates for configuration class [" +
  17.                     configurationClass.getMetadata().getClassName() + "]", ex);
  18.             }
  19.         });
  20.     }
  21. }
复制代码
grouping.getImports()方法中主要执行具体的实现类的process方法和selectImports()方法(如果是AutoConfigurationImportSelector类,则调用org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process和org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports,两个方法的具体类容请看2.2.2章节的说明),selectImports返回需要自动装配的Group.Entry对象集合,Entry对象中保存了全类名。代码如下:
  1. public Iterable<Group.Entry> getImports() {
  2.     for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
  3.         this.group.process(deferredImport.getConfigurationClass().getMetadata(),
  4.                            deferredImport.getImportSelector());
  5.     }
  6.     return this.group.selectImports();
  7. }
复制代码
ImportSelector接口代码代码如下:
  1. public interface ImportSelector {
  2.         String[] selectImports(AnnotationMetadata importingClassMetadata);
  3.         @Nullable
  4.         default Predicate<String> getExclusionFilter() {
  5.                 return null;
  6.         }
  7. }
复制代码
DeferredImportSelector接口的代码如下:
[code]public interface DeferredImportSelector extends ImportSelector {    @Nullable    default Class




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4