• SpringBoot 启动流程分析(寻找扩展点)


    1、SpringBoot maven 依赖版本

    复制代码
    xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
    
        <groupId>org.examplegroupId>
        <artifactId>SpringBootStudyartifactId>
        <version>1.0-SNAPSHOTversion>
    
        <properties>
            <maven.compiler.source>8maven.compiler.source>
            <maven.compiler.target>8maven.compiler.target>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
                <version>2.7.14version>
            dependency>
        dependencies>
    
    project>
    复制代码

    2、启动代码

    复制代码
    package com.springboot.study;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Main {
    
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(Main.class);
            springApplication.run(args);
        }
    }
    复制代码

    3、启动流程分析

    1、获取Spring工厂实例

    复制代码
      private  Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    复制代码
    复制代码
      public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
            ClassLoader classLoaderToUse = classLoader;
            if (classLoaderToUse == null) {
                classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
            }
            String factoryTypeName = factoryType.getName();
            return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
        }
    复制代码
    复制代码
      private static Map> loadSpringFactories(ClassLoader classLoader) {
            Map> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }
    
            result = new HashMap<>();
            try {
                Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry entry : properties.entrySet()) {
                        String factoryTypeName = ((String) entry.getKey()).trim();
                        String[] factoryImplementationNames =
                                StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                        for (String factoryImplementationName : factoryImplementationNames) {
                            result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                    .add(factoryImplementationName.trim());
                        }
                    }
                }
    
                // Replace all lists with unmodifiable lists containing unique elements
                result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                        .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
                cache.put(classLoader, result);
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
            return result;
        }
    复制代码

    这个地方会扫描项目中 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 方法分析

    复制代码
      public ConfigurableApplicationContext run(String... args) {
            long startTime = System.nanoTime();
            DefaultBootstrapContext bootstrapContext = createBootstrapContext();
            ConfigurableApplicationContext context = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting(bootstrapContext, this.mainApplicationClass);
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                configureIgnoreBeanInfo(environment);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                context.setApplicationStartup(this.applicationStartup);
                prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
                }
                listeners.started(context, timeTakenToStartup);
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, listeners);
                throw new IllegalStateException(ex);
            }
            try {
                Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
                listeners.ready(context, timeTakenToReady);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }
    复制代码

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

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

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

    复制代码
      protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
            MutablePropertySources sources = environment.getPropertySources();
            if (!CollectionUtils.isEmpty(this.defaultProperties)) {
                DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
            }
            if (this.addCommandLineProperties && args.length > 0) {
                String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
                if (sources.contains(name)) {
                    PropertySource source = sources.get(name);
                    CompositePropertySource composite = new CompositePropertySource(name);
                    composite
                        .addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                    composite.addPropertySource(source);
                    sources.replace(name, composite);
                }
                else {
                    sources.addFirst(new SimpleCommandLinePropertySource(args));
                }
            }
        }
    复制代码

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

    2.2、配置 Banner

    复制代码
      private Banner getBanner(Environment environment) {
            Banners banners = new Banners();
            banners.addIfNotNull(getImageBanner(environment));
            banners.addIfNotNull(getTextBanner(environment));
            if (banners.hasAtLeastOneBanner()) {
                return banners;
            }
            if (this.fallbackBanner != null) {
                return this.fallbackBanner;
            }
            return DEFAULT_BANNER;
        }
    复制代码

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

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

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

    2.3、加载 Source(prepareContext)

    复制代码
    public Set getAllSources() {
            Set allSources = new LinkedHashSet<>();
            if (!CollectionUtils.isEmpty(this.primarySources)) {
                allSources.addAll(this.primarySources);
            }
            if (!CollectionUtils.isEmpty(this.sources)) {
                allSources.addAll(this.sources);
            }
            return Collections.unmodifiableSet(allSources);
        }
    
    复制代码
    复制代码
      private void load(Object source) {
            Assert.notNull(source, "Source must not be null");
            if (source instanceof Class) {
                load((Class) source);
                return;
            }
            if (source instanceof Resource) {
                load((Resource) source);
                return;
            }
            if (source instanceof Package) {
                load((Package) source);
                return;
            }
            if (source instanceof CharSequence) {
                load((CharSequence) source);
                return;
            }
            throw new IllegalArgumentException("Invalid source type " + source.getClass());
        }
    复制代码

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

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

    2.4、ServletWebServerApplicationContext->refresh

    复制代码
      @Override
        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
                    beanPostProcess.end();
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                    contextRefresh.end();
                }
            }
        }
    复制代码

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

    复制代码
          for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                        BeanDefinitionRegistryPostProcessor registryProcessor =
                                (BeanDefinitionRegistryPostProcessor) postProcessor;
                        registryProcessor.postProcessBeanDefinitionRegistry(registry);
                        registryProcessors.add(registryProcessor);
                    }
                    else {
                        regularPostProcessors.add(postProcessor);
                    }
                }
    复制代码

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

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

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

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

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

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

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

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

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

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

    紧接着,就是如下代码:

    复制代码
           String[] postProcessorNames =
                        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                    }
                }
    复制代码

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

           context = createApplicationContext();
                context.setApplicationStartup(this.applicationStartup);
                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

    复制代码
        @Override
        protected void onRefresh() {
            super.onRefresh();
            try {
                createWebServer();
            }
            catch (Throwable ex) {
                throw new ApplicationContextException("Unable to start web server", ex);
            }
        }    
    复制代码

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

    2.6、refresh->finishBeanFactoryInitialization

    复制代码
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
            // Initialize conversion service for this context.
            if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                    beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
                beanFactory.setConversionService(
                        beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
            }
    
            // Register a default embedded value resolver if no BeanFactoryPostProcessor
            // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
            // at this point, primarily for resolution in annotation attribute values.
            if (!beanFactory.hasEmbeddedValueResolver()) {
                beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
            }
    
            // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
            String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
            for (String weaverAwareName : weaverAwareNames) {
                getBean(weaverAwareName);
            }
    
            // Stop using the temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(null);
    
            // Allow for caching all bean definition metadata, not expecting further changes.
            beanFactory.freezeConfiguration();
    
            // Instantiate all remaining (non-lazy-init) singletons.
            beanFactory.preInstantiateSingletons();
        }
    复制代码

    这个方法会调用 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 请求是如何到方法上的...等等。

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

  • 相关阅读:
    2022牛客暑期多校训练营9(总结+补题)
    b树 b+树
    利用freesurfer6进行海马分割的环境配置和步骤,以及获取海马体积
    Android ImageView详解
    hashmap1.7和1.8的区别
    Node.js学习笔记_No.05
    调研:huggingface-diffusers
    三、C语言存储类
    搜索引擎之ElasticStack的学习
    物联网开发笔记(38)- 使用Micropython开发ESP32开发板之控制温度传感器(DS18B20)
  • 原文地址:https://www.cnblogs.com/M-Anonymous/p/17566524.html