版本

springboot版本2.4.0

代码

项目代码部分

  1. @SpringBootApplication
  2. public class SpringbootUploadApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringbootUploadApplication.class, args);
  5. }
  6. }

源码部分

  1. /**
  2. 注释翻译:
  3. 一,springboot项目的启动接口,
  4. 二,官方认为的启动步骤有4个:
  5. 1.根据路径来创建容器
  6. 2.将命令行参数收集,然后注册到spring属性
  7. 3.刷新容器,加载所有单例对象
  8. 4.触发所有CommandLineRunner实现类
  9. 三,扩展点:
  10. 1.先new SpringApplication
  11. 2.然后自定义应用设置
  12. 3.最后再run
  13. 四,怎么注册组件到spring容器
  14. 1.@Configuration(常用)
  15. 2.AnnotatedBeanDefinitionReader
  16. 3.XmlBeanDefinitionReader和GroovyBeanDefinitionReader
  17. 4.ClassPathBeanDefinitionScanner(常用)
  18. 五,配置属性
  19. spring.main.sources =#要包括在ApplicationContext中的Sources(类名,包名或XML资源位置)。
  20. spring.main.web-application-type =#在Web环境中运行应用程序(默认情况下自动检测)。
  21. spring.main.banner-mode=off=#设置banner
  22. */
  23. public class SpringApplication {
  24. //1.静态启动
  25. public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  26. return run(new Class<?>[] { primarySource }, args);
  27. }
  28. //2.new对象,设置属性,调用run方法
  29. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  30. //步骤1. 初始化SpringApplication实例
  31. return new SpringApplication(primarySources).run(args);
  32. }
  33. //2.1设置属性
  34. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  35. this.resourceLoader = resourceLoader;
  36. Assert.notNull(primarySources, "PrimarySources must not be null");
  37. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  38. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  39. this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
  40. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  41. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  42. this.mainApplicationClass = deduceMainApplicationClass();
  43. }
  44. //2.2run方法
  45. public ConfigurableApplicationContext run(String... args) {
  46. StopWatch stopWatch = new StopWatch();
  47. stopWatch.start();
  48. DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  49. ConfigurableApplicationContext context = null;
  50. configureHeadlessProperty();
  51. ////步骤2.发布Spring启动事件
  52. SpringApplicationRunListeners listeners = getRunListeners(args);
  53. listeners.starting(bootstrapContext, this.mainApplicationClass);
  54. try {
  55. //步骤3. 封装命令行参数DefaultApplicationArguments
  56. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  57. //步骤4. prepareEnvironment()准备环境
  58. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
  59. configureIgnoreBeanInfo(environment);
  60. //步骤5: printBanner()打印Banner
  61. Banner printedBanner = printBanner(environment);
  62. //步骤6: createApplicationContext()创建应用上下文
  63. context = createApplicationContext();
  64. context.setApplicationStartup(this.applicationStartup);
  65. //步骤8: prepareContext()准备应用上下文
  66. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
  67. //步骤9: refreshContext()刷新应用上下文
  68. refreshContext(context);
  69. //步骤10: 启动完成, 发布ApplicationStartedEvent, ApplicationReadyEvent事件
  70. afterRefresh(context, applicationArguments);
  71. stopWatch.stop();
  72. if (this.logStartupInfo) {
  73. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  74. }
  75. //发布ApplicationStartedEvent
  76. listeners.started(context);
  77. callRunners(context, applicationArguments);
  78. }
  79. catch (Throwable ex) {
  80. handleRunFailure(context, ex, listeners);
  81. throw new IllegalStateException(ex);
  82. }
  83. try {
  84. //发布ApplicationReadyEvent事件
  85. listeners.running(context);
  86. }
  87. catch (Throwable ex) {
  88. handleRunFailure(context, ex, null);
  89. throw new IllegalStateException(ex);
  90. }
  91. return context;
  92. }
  93. }

注解部分

springboot中的类SPI扩展机制

在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中

代码

  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  4. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  5. public @interface SpringBootApplication
  6. @AutoConfigurationPackage
  7. @Import(AutoConfigurationImportSelector.class)
  8. public @interface EnableAutoConfiguration

重点看这个类AutoConfigurationImportSelector

  1. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  2. @Override
  3. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  4. if (!isEnabled(annotationMetadata)) {
  5. return NO_IMPORTS;
  6. }
  7. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  8. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  9. }
  10. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  11. if (!isEnabled(annotationMetadata)) {
  12. return EMPTY_ENTRY;
  13. }
  14. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  15. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  16. configurations = removeDuplicates(configurations);
  17. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  18. checkExcludedClasses(configurations, exclusions);
  19. configurations.removeAll(exclusions);
  20. configurations = getConfigurationClassFilter().filter(configurations);
  21. fireAutoConfigurationImportEvents(configurations, exclusions);
  22. return new AutoConfigurationEntry(configurations, exclusions);
  23. }
  24. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  25. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  26. getBeanClassLoader());
  27. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  28. + "are using a custom packaging, make sure that file is correct.");
  29. return configurations;
  30. }
  31. }

从这边META-INF/spring.factories找到很多自动配置类

  1. public final class SpringFactoriesLoader {
  2. /**
  3. * The location to look for factories.
  4. * <p>Can be present in multiple JAR files.
  5. */
  6. public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
  7. //缓存
  8. static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
  9. //Load the fully qualified class names of factory implementations of the given type from "META-INF/spring.factories", using the given class loader.
  10. //As of Spring Framework 5.3, if a particular implementation class name is discovered more than once for the given factory type, duplicates will be ignored
  11. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  12. ClassLoader classLoaderToUse = classLoader;
  13. if (classLoaderToUse == null) {
  14. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  15. }
  16. String factoryTypeName = factoryType.getName();
  17. return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
  18. }
  19. private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  20. Map<String, List<String>> result = cache.get(classLoader);
  21. if (result != null) {
  22. return result;
  23. }
  24. result = new HashMap<>();
  25. try {
  26. Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
  27. while (urls.hasMoreElements()) {
  28. URL url = urls.nextElement();
  29. UrlResource resource = new UrlResource(url);
  30. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  31. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  32. String factoryTypeName = ((String) entry.getKey()).trim();
  33. String[] factoryImplementationNames =
  34. StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
  35. for (String factoryImplementationName : factoryImplementationNames) {
  36. result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
  37. .add(factoryImplementationName.trim());
  38. }
  39. }
  40. }
  41. // Replace all lists with unmodifiable lists containing unique elements
  42. result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
  43. .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
  44. cache.put(classLoader, result);
  45. }
  46. catch (IOException ex) {
  47. throw new IllegalArgumentException("Unable to load factories from location [" +
  48. FACTORIES_RESOURCE_LOCATION + "]", ex);
  49. }
  50. return result;
  51. }
  52. }

看看已经加载的自动配置类
image.png

流程初探

  1. 初始化SpringApplication实例
    1. 通过加载各个Web类型的容器类,判断当前模块web类型
    2. 加载Application初始化器
    3. 加载Application监听器
    4. 找到启动类
  2. 发布启动事件ApplicationStartingEvent
  3. 封装命令行参数
    1. DefaultApplicationArguments
  4. prepareEnvironment()准备环境
    1. 获取或者创建环境
    2. 配置环境
    3. 发布环境准备事件
    4. Application绑定环境
    5. 环境转换
    6. 将环境依附到PropertySources
  5. 打印banner
  6. 实例化上下文AnnotationConfigServletWebServerApplicationContext
    1. 实例化IOC容器DefaultListableBeanFactory
    2. AnnotatedBeanDefinitionReader读取bean定义,处理逻辑
  7. 异常上报
    1. 改到了spring.factories中的org.springframework.boot.SpringBootExceptionReporter=org.springframework.boot.diagnostics.FailureAnalyzers
  8. 准备应用上下文prepareContext()
    1. 统一ApplicationContext和Application使用的environment
    2. 后置处理ApplicationContext
    3. 执行Initializers
    4. 发布contextPrepared事件
    5. 打印启动和profile日志
    6. 注册单例bean
    7. 加载启动类
    8. 发布contextLoaded事件
  9. 刷新应用上下文refreshContext()
    1. 更新刷新状态
    2. 处理beanFactory的后置处理器
      1. ConfigurationClassPostProcessor处理器, 处理模块中@Configuration注解 扫描加载BeanDefinition
    3. onRefresh()方法中, 实例化了TomcatWebServer
    4. finishRefresh()中, 实例化了所有bean
  10. 发布启动完成事件(ApplicationStartedEvent, ApplicationReadyEvent)
    1. 发布成功事件
    2. 发布失败事件

      第九步初始化上下文继续深入

      20.springboot启动流程 - 图2
      https://www.zhihu.com/question/48427693/answer/691483076
      refresh方法中,操作共分13步:
      第1步:对刷新进行准备,包括设置开始时间、设置激活状态、初始化context环境中的占位符,这个动作根据子类的需求由子类来执行,然后验证是否缺失必要的properties;
      第2步:刷新并获得内部的bean factory;
      第3步:对bean factory进行准备工作,比如设置类加载器和后置处理器、配置不进行自动装配的类型、注册默认的环境bean;
      第4步:为context的子类提供后置处理bean factory的扩展能力。如果子类想在bean定义加载完成后,开始初始化上下文之前做一些特殊逻辑,可以复写这个方法;
      第5步,执行context中注册的bean factory后缀处理器;
      注:这里有两种后置处理器,一种是可以注册bean的后缀处理器,另一种是针对bean factory进行处理的后置处理器。执行的顺序是,先按优先级执行可注册bean的处理器,在按优先级执行针对beanfactory的处理器。
      对springboot来说,这一步会进行注解bean definition的解析。流程如右面小框中所示,由ConfigurationClassPostProcessor触发、由ClassPathBeanDefinitionScanner解析并注册到bean factory。
      第6步:按优先级顺序在beanfactory中注册bean的后缀处理器,bean后置处理器可以在bean初始化前、后执行处理;
      第7步:初始化消息源,消息源用来支持消息的国际化;
      第8步:初始化应用事件广播器。事件广播器用来向applicationListener通知各种应用产生的事件,是一个标准的观察者模式;
      第9步,是留给子类的扩展步骤,用来让特定的context子类初始化其他的bean;
      第10步,把实现了ApplicationListener的bean注册到事件广播器,并对广播器中的早期未广播事件进行通知;
      第11步,冻结所有bean描述信息的修改,实例化非延迟加载的单例bean;
      第12步,完成上下文的刷新工作,调用LifecycleProcessor的onFresh()方法以及发布ContextRefreshedEvent事件;
      第13步:在finally中,执行第十三步,重置公共的缓存,比如ReflectionUtils中的缓存、AnnotationUtils中的缓存等等;

具体代码

  1. public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
  2. @Override
  3. public void refresh() throws BeansException, IllegalStateException {
  4. synchronized (this.startupShutdownMonitor) {
  5. StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
  6. // Prepare this context for refreshing.
  7. prepareRefresh();
  8. // Tell the subclass to refresh the internal bean factory.
  9. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  10. // Prepare the bean factory for use in this context.
  11. prepareBeanFactory(beanFactory);
  12. try {
  13. // Allows post-processing of the bean factory in context subclasses.
  14. postProcessBeanFactory(beanFactory);
  15. StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
  16. // Invoke factory processors registered as beans in the context.
  17. invokeBeanFactoryPostProcessors(beanFactory);
  18. // Register bean processors that intercept bean creation.
  19. registerBeanPostProcessors(beanFactory);
  20. beanPostProcess.end();
  21. // Initialize message source for this context.
  22. initMessageSource();
  23. // Initialize event multicaster for this context.
  24. initApplicationEventMulticaster();
  25. // Initialize other special beans in specific context subclasses.
  26. onRefresh();
  27. // Check for listener beans and register them.
  28. registerListeners();
  29. // Instantiate all remaining (non-lazy-init) singletons.
  30. finishBeanFactoryInitialization(beanFactory);
  31. // Last step: publish corresponding event.
  32. finishRefresh();
  33. }
  34. catch (BeansException ex) {
  35. if (logger.isWarnEnabled()) {
  36. logger.warn("Exception encountered during context initialization - " +
  37. "cancelling refresh attempt: " + ex);
  38. }
  39. // Destroy already created singletons to avoid dangling resources.
  40. destroyBeans();
  41. // Reset 'active' flag.
  42. cancelRefresh(ex);
  43. // Propagate exception to caller.
  44. throw ex;
  45. }
  46. finally {
  47. // Reset common introspection caches in Spring's core, since we
  48. // might not ever need metadata for singleton beans anymore...
  49. resetCommonCaches();
  50. contextRefresh.end();
  51. }
  52. }
  53. }

Spring中bean的生命周期

**20.springboot启动流程 - 图3
第1步:调用bean的构造方法创建bean;
第2步:通过反射调用setter方法进行属性的依赖注入;
第3步:如果实现BeanNameAware接口的话,会设置bean的name;
第4步:如果实现了BeanFactoryAware,会把bean factory设置给bean;
第5步:如果实现了ApplicationContextAware,会给bean设置ApplictionContext;
第6步:如果实现了BeanPostProcessor接口,则执行前置处理方法;
第7步:实现了InitializingBean接口的话,执行afterPropertiesSet方法;
第8步:执行自定义的init方法;
第9步:执行BeanPostProcessor接口的后置处理方法。

Spring扩展接口

20.springboot启动流程 - 图4
1.BeanFactoryPostProcessor
是beanFactory后置处理器,支持在bean factory标准初始化完成后,对bean factory进行一些额外处理。在讲context初始化流程时介绍过,这时所有的bean的描述信息已经加载完毕,但是还没有进行bean初始化。例如前面提到的PropertyPlaceholderConfigurer,就是在这个扩展点上对bean属性中的占位符进行替换。
2.BeanDefinitionRegistryPostProcessor
它扩展自BeanFactoryPostProcessor,在执行BeanFactoryPostProcessor的功能前,提供了可以添加bean definition的能力,允许在初始化一般bean前,注册额外的bean。例如可以在这里根据bean的scope创建一个新的代理bean。
3.BeanPostProcessor
提供了在bean初始化之前和之后插入自定义逻辑的能力。与BeanFactoryPostProcessor的区别是处理的对象不同,BeanFactoryPostProcessor是对beanfactory进行处理,BeanPostProcessor是对bean进行处理。
注:上面这三个扩展点,可以通过实现Ordered和PriorityOrdered接口来指定执行顺序。实现PriorityOrdered接口的processor会先于实现Ordered接口的执行。
4.ApplicationContextAware
可以获得ApplicationContext及其中的bean,当需要在代码中动态获取bean时,可以通过实现这个接口来实现。
5.InitializingBean
可以在bean初始化完成,所有属性设置完成后执行特定逻辑,例如对自动装配对属性进行验证等等。
6.DisposableBean
用于在bean被销毁前执行特定的逻辑,例如做一些回收工作等。
7.ApplicationListener**
用来监听spring的标准应用事件或者自定义事件。