 gitsilence 的个人博客
                gitsilence 的个人博客
            
主类
@SpringBootApplication
@EnableImportApiVersion("cn.lacknb.blog.apiversiontest.service")
public class ApiVersionTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiVersionTestApplication.class, args);
    }
}
跟进 run 方法中
SpringApplication 的构造方法中
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // primarySources 为 ApiVersionTestApplication.class
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 根据依赖的引用,判断当前容器是servlet,还是reactive,None
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 加载所有jar中的META-INF/spring.factories文件,找到对应的值列表
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 通过堆栈跟踪的方式推断出主类;因为启动的方式很多,主类不一定会通过参数传过来
    this.mainApplicationClass = deduceMainApplicationClass();
}
org.springframework.boot.BootstrapRegistryInitializer  这个接口是 Spring Boot 框架用来在应用程序启动过程中进行早期配置的一个接口。其典型用法包括注册一些早期的单例对象或配置特定的环境属性。
org.springframework.context.ApplicationContextInitializer Spring 应用上下文的初始化器,这些初始化器会在 ApplicationContext 刷新之前被调用,用于对上下文进行一些自定义的初始化操作
org.springframework.context.ApplicationListener 监听器,会在 Spring Boot 应用程序的生命周期中响应各种事件,例如应用程序的启动、上下文刷新、上下文关闭等。这个机制允许开发者在 Spring Boot 应用程序的不同阶段插入自定义逻辑,从而增强应用程序的灵活性和可扩展性。
Spring Boot在应用程序启动的不同阶段会触发一系列事件,例如:
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationContextInitializedEvent
ApplicationPreparedEvent
ApplicationStartedEvent
ApplicationReadyEvent
ApplicationFailedEvent这些事件会被发布到
ApplicationListener实例,并调用其onApplicationEvent方法。
public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    // 创建上下文,将上下文作为参数,调用所有的 BootstrapRegistryInitializer 的 initialize 初始化方法;默认是空的Initializer
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    // 读取 spring.factories 中的 SpringApplicationRunListener 的子类,放到 SpringApplicationRunListeners 中;
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 调用所有listener的starting方法 默认有一个listener:org.springframework.boot.context.event.EventPublishingRunListener
    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;
}
org.springframework.boot.context.event.EventPublishingRunListener 主要作用是通过在应用启动过程中的不同阶段发布事件,使得开发者可以通过事件监听器在这些关键点插入自定义逻辑,从而实现更灵活和可扩展的启动流程管理。这种机制不仅促进了启动流程和事件处理的解耦,还增强了应用的模块化和可测试性。
starting方法: 发布ApplicationStartingEvent事件
private final SimpleApplicationEventMulticaster initialMulticaster; 事件广播器
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}
当你创建一个新的 RuntimeException对象并调用其 getStackTrace方法时,你可以获取当前线程的堆栈跟踪信息。堆栈跟踪信息是一个 StackTraceElement对象的数组,表示方法调用的堆栈。
在堆栈跟踪数组中,按照调用顺序排列了方法调用的层级。程序的入口点,即 main方法,总是会出现在堆栈跟踪信息的某个位置。通过遍历堆栈跟踪数组,可以找到调用 main方法的类。
Spring Boot 应用程序启动的方式有很多,例如:
java -jar app.jarSpringApplication.run(MyApplication.class, args);在一些启动方式中,主类不一定会作为 primarySources传递。例如,通过Spring Boot CLI启动时,主类可能是通过命令行参数传递的,而不是在代码中显式指定的。
deduceMainApplicationClass方法提供了一种默认行为,即使在开发者没有显式指定主类的情况下,也能尽量推断出主类。这种方法增加了框架的智能性和易用性。
虽然在大多数情况下,primarySources确实包含了主类,但为了增加Spring Boot的灵活性和通用性,deduceMainApplicationClass方法提供了一种可靠的方式来推断主类。这种设计使得Spring Boot能够在多种不同的启动场景中都能正确工作,从而提高了框架的智能性和用户体验。
代码如下:
package cn.lacknb.blog.apiversiontest.config;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.time.Duration;
import java.util.Arrays;
public class MySpringApplicationRunning implements SpringApplicationRunListener {
    public MySpringApplicationRunning(SpringApplication application, String[] args) {
        System.out.print("MySpringApplicationRunning... ->");
        System.out.print(application.getClass().getName());
        System.out.println(Arrays.toString(args));
    }
    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        System.out.println("starting !");
    }
    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        System.out.println("environmentPrepared !");
    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("contextPrepared !");
    }
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("contextLoaded !");
    }
    @Override
    public void started(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("started !");
    }
    @Override
    public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println("ready !");
    }
}
创建配置文件:resources/META-INF/spring.factories
内容如下:
org.springframework.boot.SpringApplicationRunListener=\
cn.lacknb.blog.apiversiontest.config.MySpringApplicationRunning
启动 Spring Boot 主类
控制台打印如下:
MySpringApplicationRunning... ->org.springframework.boot.SpringApplication[]
starting !
environmentPrepared !
contextPrepared !
contextLoaded !
started !
ready !
由此可以看出方法的执行顺序。具体在源码中是如何执行的,还需慢慢找。
starting 方法源码位置
SpringApplication 正在启动但未开始任何处理时调用。此时,还没有创建 ApplicationContext,也没有进行任何配置加载。listeners.starting(bootstrapContext, this.mainApplicationClass);
// 更深层代码
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
                    (step) -> {
                        if (mainApplicationClass != null) {
                            step.tag("mainApplicationClass", mainApplicationClass.getName());
                        }
                    });
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
    StartupStep step = this.applicationStartup.start(stepName);
    this.listeners.forEach(listenerAction);
    if (stepAction != null) {
        stepAction.accept(step);
    }
    step.end();
}
遍历所有的 listener 执行 starting 方法
environmentPrepared
Environment 准备好后(即配置文件和环境变量已加载完成)调用,但在创建 ApplicationContext 之前。Environment 进行进一步配置或修改,例如添加新的属性源,调整系统属性等。contextPrepared
ApplicationContext 准备好之后调用,但在加载任何 bean 之前。ApplicationContext 进行进一步的自定义配置,例如注册一些特定的 bean 定义,添加或修改上下文的属性等。contextLoaded
ApplicationContext 已经加载并完成 bean 定义加载后调用,但在刷新上下文之前。started
ApplicationContext 刷新并启动后调用。但 CommandLineRunners和 ApplicationRunners尚未被调用。ready
在 run方法结束前立即调用,表示应用程序已准备好接受请求。