第三十九讲-Spring Boot启动过程分析(非常重要)

第三十九讲-Spring Boot启动过程分析(非常重要)

接下来我们来了解一下Spring Boot的启动流程。

Spring Boot的启动划分为两个阶段:

  1. 创建一个SpringApplication对象
  2. 调用SpringApplication对象的run方法,这其中包括12大步骤和7个事件

接下来我们详细的看一下Spring Boot的启动过程!

我们先回忆一下一个Spring Boot应用程序是如何启动的?我们前面讲过,这里呢我们通过改代码简要的复述一下:

@SpringBootApplication
public class A39_1 {
    public static void main(String[] args) {
        SpringApplication.run(A39_1.class, args);
    }
}

这其中的run方法到底做了些什么呢?我们跟进去看一下:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

我们发现调用run方法时创建了一个SpringApplication对象,并调用了该对象的run方法。下面我们具体的分析一下SpringApplication构造方法做了哪些事情。

1. SpringApplication构造方法

我们来看一下SpringApplication的构造方法:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

上面的代码可读性不太好,这里呢转为更可读的注释来读一下:

System.out.println("1.演示获取Bean Definition源");
System.oUt.println("2.演示推断应用类型");
System.oUt.println("3.演示ApplicationContext初始化器");
System.oUt.println("4.演示监听器与事件");
System.out.println("5.演示主类推断");

第一步:我们知道,刚开始Spring容器中是空的,它要从各个源头找到一些BeanDefinition,这些源有的来自配置类,有的来自于XML配置文件等等各种各样的源。Spring容器需要首先获取一个主源,这个主源,就是Spring的引导类。

第二步:是推断应用类型,就是Spring Boot程序一共支持三种应用类型:

  1. 第一种是非WEB应用程序

  2. 第二种是基于Servlet的WEB应用程序

  3. 第三种是基于Reactive的WEB应用程序

Spring Boot会根据当前类路径下的jar包的关键类来判断该应用程序到底是哪一种应用程序。且根据不同类型的应用程序创建不同的ApplicationContext。

第三步:添加ApplicationContext的初始化器,当我们把BeanDefinition源和应用程序类型都准备好了,就可以创建出Spring容器了,那么Spring容器创建出来以后可能会对容器做一些扩展工作,这个扩展工作就是由ApplicationContext初始化器来完成的。

第四步:添加监听器和事件,监听器用来监听Spring Boot启动过程中的一些重要事件

第五步:执行主类推断,主类推断就是将来Spring Boot执行main方法所在的类是哪一个?

当然,SpringApplication的构造方法创建出来的对象并不会创建Spring容器,真正创建容器是在SpringApplication对象执行run方法时创建出来的!

接下来我们演示这几步:

1.1 获取BeanDefinition源

@Configuration
public class A39_1 {
    public static void main(String[] args) throws Exception {
        System.out.println("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(A39_1.class);
        // 设置多个源
        spring.setSources(Set.of("classpath:b01.xml"));
        ConfigurableApplicationContext context = spring.run(args);
        // 创建 ApplicationContext
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }
        context.close();
    }

    static class Bean1 { }

    @Bean
    public Bean2 bean2() { return new Bean2();}

    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}
name: a39_1 来源:null
name: bean1 来源:class path resource [b01.xml]
name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源:null
name: bean2 来源:com.cherry.a39boot.A39_1
name: servletWebServerFactory 来源:com.cherry.a39boot.A39_1

1.2 推断应用类型

接下来我们演示一下SpringBoot如何推断应用类型的,我们首先看一下SpringApplication的相关源码:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // deduceFromClasspath:判断应用程序的类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}
static WebApplicationType deduceFromClasspath() {
    // 判断类路径下存在web.reactive.DispatcherHandler并且不存在web.servlet.DispatcherServlet
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        // 则认为该应用程序类型为Reactive类型
        return WebApplicationType.REACTIVE;
    }
    // SERVLET_INDICATOR_CLASSES:"jakarta.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        // 如果类路径下都不存在jakarta.servlet.Servlet,ConfigurableWebApplicationContext,则认为应用程序类型为非WEB应用程序
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    // 认为该应用程序为Servlet WEB应用程序 
    return WebApplicationType.SERVLET;
}

我们在测试方法中测试调用一下:

package com.cherry.a39boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Method;
import java.util.Set;

@Configuration
public class A39_1 {

    public static void main(String[] args) throws Exception {
        System.out.println("2. 演示推断应用类型");
        Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
        deduceFromClasspath.setAccessible(true);
        System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
        ConfigurableApplicationContext context = spring.run(args);
    }
    
    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}
2. 演示推断应用类型
应用类型为:SERVLET

1.3 ApplicationContext初始化器

接下来我们演示一下ApplicationContext初始化器, 初始化器的作用说白了就是对ApplicationContext的功能做一些扩展。

package com.cherry.a39boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Method;
import java.util.Set;

@Configuration
public class A39_1 {

    public static void main(String[] args) throws Exception {
        System.out.println("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(A39_1.class);
        // 设置多个源
        spring.setSources(Set.of("classpath:b01.xml"));
        System.out.println("2. 演示推断应用类型");
        Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
        deduceFromClasspath.setAccessible(true);
        System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
        System.out.println("3. 演示 ApplicationContext 初始化器");
        spring.addInitializers(applicationContext -> {
            if (applicationContext instanceof GenericApplicationContext gac) {
                gac.registerBean("bean3", Bean3.class);
            }
        });
        
        ConfigurableApplicationContext context = spring.run(args);

        // 创建 ApplicationContext
        // 调用初始化器 对 ApplicationContext 做扩展
        // ApplicationContext.refresh
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }
        context.close();
    }

    static class Bean1 {

    }

    static class Bean2 {

    }

    static class Bean3 {

    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }

    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}
name: bean3 来源:null

1.4 监听器与事件

在Spring Boot程序启动和运行的过程中,它会发布一些事件监听器就是用来响应事件并且做出处理。和SpringApplication的初始化器类似,SpringApplication的构造方法里,也有读取配置文件中的一些监听器事件,我们来举个例子,如下面的代码:

@Configuration
public class A39_1 {

    public static void main(String[] args) throws Exception {
        System.out.println("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(A39_1.class);
        // 设置多个源
        spring.setSources(Set.of("classpath:b01.xml"));
        System.out.println("2. 演示推断应用类型");
        Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
        deduceFromClasspath.setAccessible(true);
        System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
        System.out.println("3. 演示 ApplicationContext 初始化器");
        spring.addInitializers(applicationContext -> {
            if (applicationContext instanceof GenericApplicationContext gac) {
                gac.registerBean("bean3", Bean3.class);
            }
        });
        
        System.out.println("4. 演示监听器与事件");
        // 打印事件的类型
        spring.addListeners(event -> System.out.println("\t事件为:" + event.getClass()));
        ConfigurableApplicationContext context = spring.run(args);

        // 创建 ApplicationContext
        // 调用初始化器 对 ApplicationContext 做扩展
        // ApplicationContext.refresh
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }
        context.close();
    }

    static class Bean1 {

    }

    static class Bean2 {

    }

    static class Bean3 {

    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }

    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}

4. 演示监听器与事件

	事件为:class org.springframework.boot.context.event.ApplicationContextInitializedEvent
	事件为:class org.springframework.boot.context.event.ApplicationPreparedEvent
	事件为:class org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
	事件为:class org.springframework.context.event.ContextRefreshedEvent
	事件为:class org.springframework.boot.context.event.ApplicationStartedEvent
	事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
	事件为:class org.springframework.boot.context.event.ApplicationReadyEvent
	事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
	事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
	事件为:class org.springframework.context.event.ContextClosedEvent

1.5 主类推断

主类推断就是判断将来Spring Boot执行main方法所在的类是哪一个?如下面的测试代码:

@Configuration
public class A39_1 {

    public static void main(String[] args) throws Exception {
        System.out.println("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(A39_1.class);
        // 设置多个源
        spring.setSources(Set.of("classpath:b01.xml"));
        System.out.println("2. 演示推断应用类型");
        Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
        deduceFromClasspath.setAccessible(true);
        System.out.println("\t应用类型为:"+deduceFromClasspath.invoke(null));
        System.out.println("3. 演示 ApplicationContext 初始化器");
        spring.addInitializers(applicationContext -> {
            if (applicationContext instanceof GenericApplicationContext gac) {
                gac.registerBean("bean3", Bean3.class);
            }
        });
        System.out.println("4. 演示监听器与事件");
        spring.addListeners(event -> System.out.println("\t事件为:" + event.getClass()));
        System.out.println("5. 演示主类推断");
        Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
        deduceMainApplicationClass.setAccessible(true);
        System.out.println("\t主类是:"+deduceMainApplicationClass.invoke(spring));
        ConfigurableApplicationContext context = spring.run(args);

        // 创建 ApplicationContext
        // 调用初始化器 对 ApplicationContext 做扩展
        // ApplicationContext.refresh
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name: " + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }
        context.close(); 
    }

    static class Bean1 {

    }

    static class Bean2 {

    }

    static class Bean3 {

    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }

    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}
5. 演示主类推断
	主类是:class com.cherry.a39boot.A39_1

1.6 总结

SpringApplication 构造方法中所做的操作

  1. 可以有多种源用来加载 bean 定义
  2. 应用类型推断
  3. 容器初始化器
  4. 启动各阶段事件
  5. 主类推断

2. SpringApplication对象的run方法

接下来我们来看一下SpringApplication对象的run方法执行流程

下面的示例代码演示了各个阶段的使用示例:

// 运行时请添加运行参数 --server.port=8080 debug
public class A39_3 {
    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                System.out.println("执行初始化器增强...");
            }
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
        // 将普通参数封装为ApplicationArguments参数
        DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 8. 创建容器");
        GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 9. 准备容器");
        for (ApplicationContextInitializer initializer : app.getInitializers()) {
            // 获取所有初始化器并进行初始化
            initializer.initialize(context);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 10. 加载 bean 定义");
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 1. 基于注解获取bean
        AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
        // 2. 基于xml获取bean
        XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
        // 3. 基于类扫描器获取bean
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        reader1.register(Config.class);
        reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
        scanner.scan("com.cherry.a39boot.sub");

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name:" + name + " 来源:" + beanFactory.getBeanDefinition(name).getResourceDescription());
        }

        // 实现CommandLineRunner接口
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");
        for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
            runner.run(args);
        }

        // 实现ApplicationRunner接口
        for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
            runner.run(arguments);
        }

        /*
            学到了什么
            a. 创建容器、加载 bean 定义、refresh, 对应的步骤

         */
    }

    // 根据程序的不同类型生成不同的Spring容器
    private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
        GenericApplicationContext context = null;
        switch (type) {
            case SERVLET -> context = new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE -> context = new AnnotationConfigReactiveWebServerApplicationContext();
            case NONE -> context = new AnnotationConfigApplicationContext();
        }
        return context;
    }

    static class Bean4 {

    }

    static class Bean5 {

    }

    static class Bean6 {

    }

    @Configuration
    static class Config {
        @Bean
        public Bean5 bean5() {
            return new Bean5();
        }

        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public CommandLineRunner commandLineRunner() {
            return new CommandLineRunner() {
                @Override
                public void run(String... args) throws Exception {
                    System.out.println("commandLineRunner()..." + Arrays.toString(args));
                }
            };
        }

        @Bean
        public ApplicationRunner applicationRunner() {
            return new ApplicationRunner() {
                @Override
                public void run(ApplicationArguments args) throws Exception {
                    System.out.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
                    System.out.println(args.getOptionNames());
                    System.out.println(args.getOptionValues("server.port"));
                    System.out.println(args.getNonOptionArgs());
                }
            };
        }
    }
}

我们根据源码来总结一下SpringApplication对象的run方法:

public ConfigurableApplicationContext run(String... args) {
    Startup startup = Startup.create();
    if (this.registerShutdownHook) {
        SpringApplication.shutdownHook.enableShutdownHookAddition();
    }
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    // 1. 获取一个事件发布器对象-->读取Spring Factory中的配置,找到事件发布器的实现类
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // starting事件表示Spring Boot程序开始启动
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        // 2. 将main方法中普通的参数封装成了一个ApplicationArguments后的参数对象(将参数划分为选项参数和非选项参数)
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       	// 进入环境准备阶段
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // 7. 打印banner信息
        Banner printedBanner = printBanner(environment);
        // 8. 根据程序类型创建Spring容器
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 进入到 prepareContext 方法中
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        
        // 11. 调用容器的refresh方法,调用各种Bean Factory的后处理器,准备各种Bean的后处理器,初始化每个单例,并发布一个容器已准备就绪事件
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        startup.started();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
        }
        listeners.started(context, startup.timeTakenToStarted());
        
        // 12. 调用所有实现了ApplicationRunner和CommandLineRunner接口的Bean
        callRunners(context, applicationArguments);
    }
    // 如果在启动过程中出现了异常,就会发布一个失败事件!
    catch (Throwable ex) {
        throw handleRunFailure(context, ex, listeners);
    }
    try {
        // 发布一个ready(running)事件,表示Spring Boot程序启动完成
        if (context.isRunning()) {
            listeners.ready(context, startup.ready());
        }
    }
    catch (Throwable ex) {
        throw handleRunFailure(context, ex, null);
    }
    return context;
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // Create and configure the environment
     // 3. 准备一个环境对象,并将第2步封装好的参数对象加载到环境中(k-v形式)
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 4. 将命名不规范的键值对做一个统一的处理(统一转为'-'形式)
    ConfigurationPropertySources.attach(environment);
    // 5. 此时事件器发布环境已准备好事件-->表明此时环境已经准备好了
    listeners.environmentPrepared(bootstrapContext, environment);
    DefaultPropertiesPropertySource.moveToEnd(environment);
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
            "Environment prefix cannot be set via properties.");
    // 6. 将环境中以spring.main前缀的key跟Spring application对象做一个绑定
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
        environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 9. 容器初始化器,对application Context做一些功能增强
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    addAotGeneratedInitializerIfNecessary(this.initializers);
    applyInitializers(context);
    // 发布一个容器已准备好事件
    listeners.contextPrepared(context);
    bootstrapContext.close(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
        autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
        if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
            listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    if (this.keepAlive) {
        context.addApplicationListener(new KeepAlive());
    }
    context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    // 10. 得到所有的Bean Definition源,并将这些Bean Definition源加载到Application Context中,并发布一个Bean Definition已加载事件
    if (!AotDetector.useGeneratedArtifacts()) {
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
    }
    listeners.contextLoaded(context);
}
posted @   LilyFlower  阅读(4)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示