SpringBoot源码(四):run() 方法解析(一)

run()方法:

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    // 记录应用启动时间
    long startTime = System.nanoTime();
    
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    // 创建 ConfigurableApplicationContext 对象
    ConfigurableApplicationContext context = null;
    // 设置系统的 awt (保证在没有检测到显示器的情况下(如linux服务器),SpringBoot应用也可以启动)
    configureHeadlessProperty();
    // 【】创建 SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 调用 SpringApplicationRunListener 的 starting 方法,发布了一个 ApplicationStartingEvent 事件 【】
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        // 【】准备运行时环境 Environment
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // 
        configureIgnoreBeanInfo(environment);
        // 创建Banner对象、打印 Banner(会在控制台中打印 SPRING 图案,和 SpringBoot 的版本号)
        Banner printedBanner = printBanner(environment);
        // 【】创建IOC容器
        context = createApplicationContext();
        // 启动度量
        context.setApplicationStartup(this.applicationStartup);
        // 【】初始化IOC容器
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 【】刷新IOC容器(会同时在控制台打印Tomcat有关的信息)
        refreshContext(context);
        // 空实现
        afterRefresh(context, applicationArguments);
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            // 在控制台打印,消耗时间的信息:Started MyApplication in 209.698 seconds (JVM running for 215.496)
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }
        // 调用 SpringApplicationRunListener 的 started 方法,发布了一个 ApplicationStartedEvent 事件 【】
        listeners.started(context, timeTakenToStartup);
        // 回调所有的运行器
        callRunners(context, applicationArguments);
    }//..
    
    try {
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        // 调用 SpringApplicationRunListener 的 ready 方法,发布了一个 ApplicationReadyEvent 事件 【】
        listeners.ready(context, timeTakenToReady);
    }//..
    
    // 返回IOC容器(所以 SpringApplication.run() 方法是有返回值的)
    return context;
}

获取 SpringApplicationRunListeners

在 SpringApplication 的 run() 方法内,有这样一步:

SpringApplicationRunListeners = getRunListeners(args)

获取 SpringApplicationRunListeners ,SpringApplicationRunListeners 其实就是装SpringApplicationRunListener 的容器

SpringApplicationRunListener

SpringApplicationRunListener 看名字就可以知道它是一个监听器,只不过它只负责监听 run() 方法,由于 run() 方法过于复杂,且整个 run() 方法中涉及很多切入点和扩展点,留有一个监听器可以在预定义好的切入点中扩展自定义逻辑。

SpringApplicationRunListener 提供了一系列的方法,用户可以通过回调这些方法,在启动各个流程时加入指定的逻辑处理。下面我们对照源代码和注释来了解一下该接口都定义了哪些待实现的方法及功能:

java 复制代码
public interface SpringApplicationRunListener {
	// 当 run() 调用时,会被立即调用,可用于非常早期的初始化工作
    default void starting(ConfigurableBootstrapContext bootstrapContext) {}
    // 当 environment 对象准备完成,在 IOC 容器创建之前,该方法被调用
    default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {}
	// 当 IOC 创建完成,资源还未被加载时,该方法被调用
    default void contextPrepared(ConfigurableApplicationContext context) {}
	// 当 IOC 资源加载完成,未被刷新时,该方法被调用
    default void contextLoaded(ConfigurableApplicationContext context) {}
	// 当 IOC 刷新完并启动之后,未调用 CommandLineRunner 和 ApplicationRunner 时,该方法被调用
    default void started(ConfigurableApplicationContext context, Duration timeTaken) {
 started(context); }
    // 所有准备工作就绪,调用该方法
    default void ready(ConfigurableApplicationContext context, Duration timeTaken) { running(context); }
	// 当应用程序出现错误时,调用该方法
    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }   
    //...
}

可以看出,SpringApplicationRunListener 为 run 方法提供了各个运行阶段的监听事件处理功能:

继续分析 getRunListeners 方法:

SpringApplication # getRunListeners()

java 复制代码
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };   
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}

SpringApplicationRunListeners 的构造方法传入了三个参数:这里关注第二个参数

getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args) (这里把 this(当前SpringApplication 对象)传入了方法

会通过 SpringFactoriesLoader 加载并实例化外部定义的所有 SpringApplicationRunListener (默认只有一个:EventPublishingRunListener )

并且这些 SpringApplicationRunListener 必须有一个接收 (SpringApplication application,String[] args)参数类型的构造器

EventPublishingRunListener 是 SpringBoot 中针对 SpringApplicationRunListener 接口的唯一内建实现

注意看注释:

java 复制代码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {  // 注意这里的 Object... args 参数包含了 SpringApplication 对象
    ClassLoader classLoader = getClassLoader();
    // 获取类路径下 META-INF/spring.factories 中所有的 SpringApplicationRunListener 的名字(默认只有一个:EventPublishingRunListener)
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 实例化这个 EventPublishingRunListener(会利用 args 构建 EventPublishingRunListener对象)
    // 【注意】如果之后在 META-INF/spring.factories 中又扩展了许多 SpringApplicationRunListener,那么同样的,会利用 args 构建这些 SpringApplicationRunListener 对象
    // 即,这些实例化好的这些 SpringApplicationRunListener 一定会持有 SpringApplication 对象,从而拥有它里面的一些属性(如,可以获取所有的监听器对象)
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    // 返回
    return instances;
}

EventPublishingRunListener 的构造方法如下:

【注】创建好的 EventPublishingRunListener 对象会拥有 SpringApplication 对象里的 ApplicationListener 等属性

java 复制代码
public EventPublishingRunListener(SpringApplication application, String[] args) {
    // 持有了 SpringApplication 对象
    this.application = application;
    this.args = args;
    // 初始化了一个事件多播器 SimpleApplicationEventMulticaster
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 将 SpringApplication 对象里的 ApplicationListener 全部加到了多播器中
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener); 
    }
}

通过源代码可以看出,该类的构造方法符合 SpringApplicationRunListener 所需的构造方法参数要求,该方法依次传递了 SpringApplication 和 String[] 类型。

在构造方法中初始化了该类的3个成员变量:

​ ① application:类型为 SpringApplication,是当前运行的 SpringApplication 实例

​ ② args:启动程序时的命令参数

​ ③ initialMulticaster:类型为 SimpleApplicationEventMulticaster,事件广播器

SpringBoot 完成基本的初始化之后,会遍历 SpringApplication 的所有 ApplicationListener 实例,并将它们与 SimpleApplicationEventMulticaster 进行关联,方便后续将事件传递给所有的监听器。

EventPublishingRunListener 针对不同的事件提供了不同的处理方法,但它们的处理流程基本相同:

​ ● 程序启动到某个步骤后,调用 EventPublishingRunListener 的某个方法

​ ● EventPublishingRunListener 的具体方法将 application 参数和 args 参数封装到对应的事件中(这里的事件均为 SpringApplicationEvent 的实现类)

​ ● 通过成员变量 initialMulticaster 的multicastEvent方法对事件进行广播,或通过该方法的 ConfigurableApplicationContext context 参数的 publishEvent() 方法来对事件进行发布

​ ● 对应的 ApplicationListener 被触发,执行相应的业务逻辑

如:EventPublishingRunListener 的 starting() 方法:

java 复制代码
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}

listeners.starting()

之后的一步: 调用 SpringApplicationRunListeners 的 starting 方法:【注】此时 SpringApplicationRunListeners 已经拥有了之前创建好的 EventPublishingRunListener 对象。

SpringApplicationRunListeners # starting()

java 复制代码
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),  
                    (step) -> {
                        if (mainApplicationClass != null) {
                            step.tag("mainApplicationClass", mainApplicationClass.getName());
                        }
                    });
}

SpringApplicationRunListeners # doWithListeners()

java 复制代码
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
    StartupStep step = this.applicationStartup.start(stepName);
    // 遍历 this.listeners(目前只有一个EventPublishingRunListener),执行它们的 starting(bootstrapContext) 方法
    this.listeners.forEach(listenerAction); 
    if (stepAction != null) {
        stepAction.accept(step);  
    }
   
    step.end();
}

EventPublishingRunListener # starting()

可以看到方法内部,利用事件多播器,发布了一个 ApplicationStartingEvent 事件

java 复制代码
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}

以下三个监听器会监听到,并调用它们的 onApplicationEvent 方法:

① LoggingApplicationListener、

② BackgroundPreinitializer、

③ DelegatingApplicationListener

它们都注册在 META-INF/spring.factories 中

总结

【注】SpringApplicationRunListeners,它相当于一系列 SpringApplicationRunListener 的组合 这些 SpringApplicationRunListener 的实现类都在 META-INF/spring.factories 中注册

(目前只有一个:EventPublishingRunListener)

java 复制代码
private final List<SpringApplicationRunListener> listeners

如:调用 SpringApplicationRunListeners 的 starting(),则会遍历 List< SpringApplicationRunListener> listeners 里所有的 SpringApplicationRunListener ,依次调用它们的 starting()

其实就只有一个: EventPublishingRunListener,调用它的 starting(),而方法内部就会通过广播器将对应的事件广播给EventPublishingRunListener 所持有的 ApplicationListener 对象

相关推荐
小码哥_常23 分钟前
别再被误导!try...catch性能大揭秘
后端
无巧不成书02182 小时前
30分钟入门Java:从历史到Hello World的小白指南
java·开发语言
苍何2 小时前
30分钟用 Agent 搓出一家跨境网店,疯了
后端
ssshooter3 小时前
Tauri 2 iOS 开发避坑指南:文件保存、Dialog 和 Documents 目录的那些坑
前端·后端·ios
追逐时光者3 小时前
一个基于 .NET Core + Vue3 构建的开源全栈平台 Admin 系统
后端·.net
程序员飞哥3 小时前
90后大龄程序员失业4个月终于上岸了
后端·面试·程序员
zs宝来了4 小时前
Playwright 自动发布 CSDN 的完整实践
java
彭于晏Yan5 小时前
Redisson分布式锁
spring boot·redis·分布式
吴声子夜歌5 小时前
TypeScript——基础类型(三)
java·linux·typescript
GetcharZp5 小时前
Git 命令行太痛苦?这款 75k Star 的神级工具,让你告别“合并冲突”恐惧症!
后端