经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
SpringBoot 启动流程分析(寻找扩展点) - M-Anonymous
来源:cnblogs  作者:M-Anonymous  时间:2023/7/31 9:11:28  对本文有异议

1、SpringBoot maven 依赖版本

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6.  
  7. <groupId>org.example</groupId>
  8. <artifactId>SpringBootStudy</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10.  
  11. <properties>
  12. <maven.compiler.source>8</maven.compiler.source>
  13. <maven.compiler.target>8</maven.compiler.target>
  14. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  15. </properties>
  16.  
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.springframework.boot</groupId>
  20. <artifactId>spring-boot-starter-web</artifactId>
  21. <version>2.7.14</version>
  22. </dependency>
  23. </dependencies>
  24.  
  25. </project>

2、启动代码

  1. package com.springboot.study;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class Main {
  6. public static void main(String[] args) {
  7. SpringApplication springApplication = new SpringApplication(Main.class);
  8. springApplication.run(args);
  9. }
  10. }

3、启动流程分析

1、获取Spring工厂实例

  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.   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. }

这个地方会扫描项目中 resource 目录下的 META-INF/spring.factories 文件,默认如果不添加其他依赖,会扫描到如下项目:

  1. spring-boot-2.7.14.jar
  2. spring-boot-autoconfigure-2.7.14.jar
  3. spring-beans-5.3.29.jar

最终会扫描到如下对象:

2、run 方法分析

  1.   public ConfigurableApplicationContext run(String... args) {
  2. long startTime = System.nanoTime();
  3. DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  4. ConfigurableApplicationContext context = null;
  5. configureHeadlessProperty();
  6. SpringApplicationRunListeners listeners = getRunListeners(args);
  7. listeners.starting(bootstrapContext, this.mainApplicationClass);
  8. try {
  9. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  10. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
  11. configureIgnoreBeanInfo(environment);
  12. Banner printedBanner = printBanner(environment);
  13. context = createApplicationContext();
  14. context.setApplicationStartup(this.applicationStartup);
  15. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
  16. refreshContext(context);
  17. afterRefresh(context, applicationArguments);
  18. Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
  19. if (this.logStartupInfo) {
  20. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
  21. }
  22. listeners.started(context, timeTakenToStartup);
  23. callRunners(context, applicationArguments);
  24. }
  25. catch (Throwable ex) {
  26. handleRunFailure(context, ex, listeners);
  27. throw new IllegalStateException(ex);
  28. }
  29. try {
  30. Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
  31. listeners.ready(context, timeTakenToReady);
  32. }
  33. catch (Throwable ex) {
  34. handleRunFailure(context, ex, null);
  35. throw new IllegalStateException(ex);
  36. }
  37. return context;
  38. }

1、首先是 createBootstrapContext 方法,该方法会调用第一个扩展点BootstrapRegistryInitializer->initializer,但是项目中没有该接口的实现类。

2、其次就是 listeners.starting 方法, 该方法中会调用第二个扩展点 SpringApplicationRunListener->starting,这个 SpringApplicationRunListener 项目中只存在一个实现类:EventPublishingRunListener,它会触发所有的 ApplicationListener 监听 ApplicationStartingEvent 的事件,后文就不特别声明这个实现类了。

2.1、配置属性(prepareEnvironment->configureEnvironment)

  1.   protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
  2. MutablePropertySources sources = environment.getPropertySources();
  3. if (!CollectionUtils.isEmpty(this.defaultProperties)) {
  4. DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
  5. }
  6. if (this.addCommandLineProperties && args.length > 0) {
  7. String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
  8. if (sources.contains(name)) {
  9. PropertySource<?> source = sources.get(name);
  10. CompositePropertySource composite = new CompositePropertySource(name);
  11. composite
  12. .addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
  13. composite.addPropertySource(source);
  14. sources.replace(name, composite);
  15. }
  16. else {
  17. sources.addFirst(new SimpleCommandLinePropertySource(args));
  18. }
  19. }
  20. }

3、然后就是 prepareEnvironment 方法,该方法会调用第三个扩展点:SpringApplicationRunListener->environmentPrepared,它会触发所有的 ApplicationListener 监听 ApplicationEnvironmentPreparedEvent 的事件。

2.2、配置 Banner

  1.   private Banner getBanner(Environment environment) {
  2. Banners banners = new Banners();
  3. banners.addIfNotNull(getImageBanner(environment));
  4. banners.addIfNotNull(getTextBanner(environment));
  5. if (banners.hasAtLeastOneBanner()) {
  6. return banners;
  7. }
  8. if (this.fallbackBanner != null) {
  9. return this.fallbackBanner;
  10. }
  11. return DEFAULT_BANNER;
  12. }

4、第四个扩展点在 prepareContext->applyInitializers 方法里,ApplicationContextInitializer->initialize

5、第五个扩展点在 prepareContext->listeners.contextPrepared 方法里,SpringApplicationRunListeners->contextPrepared,它会触发所有的 ApplicationListener 监听 ApplicationContextInitializedEvent 的事件。

6、第六个扩展点在 prepareContext->bootstrapContext.close 方法里,它会触发所有的 ApplicationListener 监听 BootstrapContextClosedEvent 的事件。

2.3、加载 Source(prepareContext)

  1. public Set<Object> getAllSources() {
  2. Set<Object> allSources = new LinkedHashSet<>();
  3. if (!CollectionUtils.isEmpty(this.primarySources)) {
  4. allSources.addAll(this.primarySources);
  5. }
  6. if (!CollectionUtils.isEmpty(this.sources)) {
  7. allSources.addAll(this.sources);
  8. }
  9. return Collections.unmodifiableSet(allSources);
  10. }
  1.   private void load(Object source) {
  2. Assert.notNull(source, "Source must not be null");
  3. if (source instanceof Class<?>) {
  4. load((Class<?>) source);
  5. return;
  6. }
  7. if (source instanceof Resource) {
  8. load((Resource) source);
  9. return;
  10. }
  11. if (source instanceof Package) {
  12. load((Package) source);
  13. return;
  14. }
  15. if (source instanceof CharSequence) {
  16. load((CharSequence) source);
  17. return;
  18. }
  19. throw new IllegalArgumentException("Invalid source type " + source.getClass());
  20. }

这个地方会从 SpringApplication 中的 primarySources、sources 加载源,然后注册为 Bean。

7、第七个扩展点在 prepareContext->listeners.contextLoaded 方法里,SpringApplicationRunListeners->contextLoaded,会触发 ApplicationListener 监听 ApplicationPreparedEvent 的事件。

2.4、ServletWebServerApplicationContext->refresh

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

这里我们直接看到 invokeBeanFactoryPostProcessors 方法,不过这个方法蛮长的,就先看一部分:

  1.       for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
  2. if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
  3. BeanDefinitionRegistryPostProcessor registryProcessor =
  4. (BeanDefinitionRegistryPostProcessor) postProcessor;
  5. registryProcessor.postProcessBeanDefinitionRegistry(registry);
  6. registryProcessors.add(registryProcessor);
  7. }
  8. else {
  9. regularPostProcessors.add(postProcessor);
  10. }
  11. }

这个地方可以猜到这个 postProcessBeanDefinitionRegistry 也是一个扩展点,但是这个 beanFactoryPostProcessors 的值是从哪里来的呢?

打断点这里是有三个值的:

这里直接说答案,还记得这段代码吗:

  1. context = createApplicationContext();
  2. context.setApplicationStartup(this.applicationStartup);
  3. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

这里获取了 context 之后,就调用了 prepareContext 方法,prepareContext 方法里曾提到过有第四、第五、第六、第七个扩展点

举个例子,就拿第四个扩展点说:

ConfigurationWarningsApplicationContextInitializer 实现了 ApplicationContextInitializer 接口,然后添加了一个值:

  1.   @Override
  2. public void initialize(ConfigurableApplicationContext context) {
  3. context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
  4. }

相同的还有 SharedMetadataReaderFactoryContextInitializer,至于第三个值则是直接在 prepareContext 方法里添加的。

8、第八个扩展点在 refresh->invokeBeanFactoryPostProcessors 方法里,会调用 invokeBeanDefinitionRegistryPostProcessors

紧接着,就是如下代码:

  1.        String[] postProcessorNames =
  2. beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
  3. for (String ppName : postProcessorNames) {
  4. if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  5. currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
  6. processedBeans.add(ppName);
  7. }
  8. }

我们看看这个 postProcessorNames 是怎么来的,追踪到 beanFactory.getBeanNamesForType 方法,进去可以看到 doGetBeanNamesForType 方法,总而言之,是从 beanDefinitionNames 和 manualSingletonNames 的值来的,那这些值又是怎么来的呢,仍然是如下一段代码:

  1.        context = createApplicationContext();
  2. context.setApplicationStartup(this.applicationStartup);
  3. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

在创建 context 的时候,实际上是调用 AnnotationConfigApplicationContext 的构造方法,里面会调用 registerAnnotationConfigProcessors 方法,这个方法会初始化 5 个 beanDefinitionName。

然后就是 prepareContext 方法里,在第四个扩展点会初始化两个 manualSingletonName,prepareContext 方法里的 registerSingleton 方法也添加了两个 manualSingletonName,并且后面的 load 方法也添加了一个 main 的 beanDefinitionName,在最后的第七个扩展点里又添加了 3 个 manualSingletonName,总之在执行 refreshContext 之前,context 的 beanFactory 里包含 6 个 beanDefinitionName 7 个 manualSingletonName,后面的就不分析了,毕竟能扩展的就在这里。

9、第九个扩展点也在 refresh->invokeBeanFactoryPostProcessors 方法里,会调用 invokeBeanFactoryPostProcessors

不过有一点要注意的是,虽说第八个、第九个也算是扩展点,但其只有在第四到第七个扩展点里面配置了才能进行扩展。

2.5、refresh->onRefresh

  1. @Override
  2. protected void onRefresh() {
  3. super.onRefresh();
  4. try {
  5. createWebServer();
  6. }
  7. catch (Throwable ex) {
  8. throw new ApplicationContextException("Unable to start web server", ex);
  9. }
  10. }

该方法会调用 createWebServer 创建一个 webserver。

2.6、refresh->finishBeanFactoryInitialization

  1. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  2. // Initialize conversion service for this context.
  3. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  4. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  5. beanFactory.setConversionService(
  6. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  7. }
  8. // Register a default embedded value resolver if no BeanFactoryPostProcessor
  9. // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
  10. // at this point, primarily for resolution in annotation attribute values.
  11. if (!beanFactory.hasEmbeddedValueResolver()) {
  12. beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  13. }
  14. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  15. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  16. for (String weaverAwareName : weaverAwareNames) {
  17. getBean(weaverAwareName);
  18. }
  19. // Stop using the temporary ClassLoader for type matching.
  20. beanFactory.setTempClassLoader(null);
  21. // Allow for caching all bean definition metadata, not expecting further changes.
  22. beanFactory.freezeConfiguration();
  23. // Instantiate all remaining (non-lazy-init) singletons.
  24. beanFactory.preInstantiateSingletons();
  25. }

这个方法会调用 beanFactory,实例化所有的 bean。

10、第十个扩展点在 refresh->finishRefresh 方法里,会调用 getLifecycleProcessor().onRefresh(),会调用 SmartLifecycle->start 方法。

11、第十一个扩展点也在 refresh->finishRefresh 方法里,会调用 publishEvent 然后触发 ApplicationListener 监听 ContextRefreshedEvent 的事件。

2.7、最后的三个扩展点-> run 方法

12、第十二个扩展点在 run 方法里,会调用 listeners.started 方法,SpringApplicationRunListener->started

13、第十三个扩展点也在 run 方法里,会调用 callRunners 方法,ApplicationRunner 或 CommandLineRunner 的 run 方法

14、第十四个扩展点也在 run 方法里,会调用 listeners.ready方法,SpringApplicationRunListener->ready

3、结语

第一篇分析 SpringBoot 启动源码到这里就结束了,我们这次的目标是找到 SpringBoot 有哪些地方可以自己进行代码扩展的,其中不免有些遗漏的,欢迎各位补充。

目前看完还是有很多问题,比如 SpringBootApplication 注解的作用是什么、Bean 的实例化流程是什么、以及我们 Web 的 URL 请求是如何到方法上的...等等。

最后,这篇分析完的这些扩展点能干些什么呢?

原文链接:https://www.cnblogs.com/M-Anonymous/p/17566524.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号