首页 > 编程语言 >【java】(二) SpringBoot 源码解析——run启动加载过程——准备环境

【java】(二) SpringBoot 源码解析——run启动加载过程——准备环境

时间:2022-12-19 11:33:49浏览次数:64  
标签:environment run SpringBoot bootstrapContext listener listeners 源码 context event

1.前言

深入学习springboot笔记系列,可能会有错误还请指正,互相勉励,互相学习。

上一章讲了SpringApplicaiton 是如何初始化的, 本章讲解后续的 run方法的启动过程。

本章涉及到观察者模式,如果对观察者模式不太清楚的小伙伴可以去看下观察者模式详解,相信看完这篇,能更容易理解接下来的源码。

2.run方法加载过程

此处注释大致标注了各个方法执行的阶段,后面将对比较重要的几个阶段进行拆分解读。

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    // 通过初始化SpringApplication时的BootstrapRegistryInitializer实现类来初始化一个DefaultBootstrapContext
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    // 设置java.awt.headless 的default值
    configureHeadlessProperty();
    // 获得所有继承于SpringApplicationRunListener的listener并实例化
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 广播一个ApplicationStartEvent
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 初始化Environment
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        // 设置spring.beaninfo.ignore
        configureIgnoreBeanInfo(environment);
        // 打印Banner
        Banner printedBanner = printBanner(environment);
        // 创建ApplicationContext
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 准备Context
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 刷新Context
        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);
        // 执行所有runner
        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;
}

2.1.创建 bootstrapContext

private DefaultBootstrapContext createBootstrapContext() {
    // 创建一个default的bootstrapContext
    DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
    // 将SpringApplication 初始化的bootstrapRegistryInitializers 用于初始化 bootstrapContext
    this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
    return bootstrapContext;
}

创建一个默认的 DefaultBootstrapContext 对象实例,并用 bootstrapRegistryInitializers 中的类初始化它。

2.2.设置 headless

private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
            System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

根据环境变量设置headless,默认设置为true,因为大多数的Spring Boot项目都是纯服务端应用,不是交互式应用,不需要显示设备、键盘或鼠标等。

2.3.创建 SpringApplicationRunListeners 对象实例

private SpringApplicationRunListeners getRunListeners(String[] args) {
    // 反射创建对象所使用的有参构造器参数类型 
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 创建 SpringApplicationRunListeners 对象
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
            this.applicationStartup);
}

其中 getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)  使用反射获取 SpringApplicationRunListener 的实现类的相关有参构造器并创建对象实例。

SpringBoot 项目中 SpringApplicationRunListener 的实现类只有 org.springframework.boot.context.event.EventPublishingRunListener ,因此此处用来实例化 EventPublishingRunListener 类,接下来我们看看 EventPublishingRunListener 构造器中做了些什么操作。

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    // 默认的事件广播对象
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 将SpringApplication 初始化的 listeners 添加至 事件广播对象中
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

初始化了一个事件广播对象,并将 listener 装载至 事件广播对象 initialMulticaster 中,也就是注册观察者的操作,至此一切准备就绪。

2.4.广播 ApplicationStartingEvent 事件

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());
                }
            });
}

// 函数式编程,Consumer消费者用于接收一个lambda函数。
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
        Consumer<StartupStep> stepAction) {
    // applicationStartup 记录启动过程可以不用太关注
    StartupStep step = this.applicationStartup.start(stepName);
    // 此处的 listeners 即为 EventPublishingRunListener 也可以新增自定义实现类
// 此处执行上处的(listener) -> listener.starting(bootstrapContext) lambda函数,即调用 EventPublishingRunListener.starting 方法 this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end(); } // EventPublishingRunListener.class 的 starting 实现 public void starting(ConfigurableBootstrapContext bootstrapContext) { // 广播一个 ApplicationStartingEvent 事件 this.initialMulticaster .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args)); } public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); } public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // 线程池,如果需要异步通知则开启 Executor executor = getTaskExecutor(); // getApplicationListeners(event, type)此处根据 event 事件过滤广播哪些Listener,有兴趣可以自行去查看相关逻辑。 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { // 最终调用了listener.onApplicationEvent(event),下面标橙的部分。 // 至此一个完整的观察者模式得以呈现出来了 invokeListener(listener, event); } } } protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); } } private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass()) || (event instanceof PayloadApplicationEvent && matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception. Log loggerToUse = this.lazyLogger; if (loggerToUse == null) { loggerToUse = LogFactory.getLog(getClass()); this.lazyLogger = loggerToUse; } if (loggerToUse.isTraceEnabled()) { loggerToUse.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }

2.5.准备Environment环境

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // 根据web类型(Servlet,Reactive,非Web)创建不同的Environment变量,web项目此处为 ApplicationServletEnvironment实例
    ConfigurableEnvironment environment = getOrCreateEnvironment();

    // 将Spring Boot启动参数配置进Environment对象, 用于启动项目时添加外部定义的配置
    configureEnvironment(environment, applicationArguments.getSourceArgs());

    // ConfigurationPropertySources 与 environment 绑定
    ConfigurationPropertySources.attach(environment);

    // 广播一个 ApplicationEnvironmentPreparedEvent 事件,此处比较重要,后面会挑选个重要的Linstener 来讲解一下,这一步会读取我们的配置文件 application.yml
    listeners.environmentPrepared(bootstrapContext, environment);

    // 将'defaultProperties'propertySource移到最后
    DefaultPropertiesPropertySource.moveToEnd(environment);
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");

    // 绑定environment
    bindToSpringApplication(environment);

    // 转换Environment
    if (!this.isCustomEnvironment) {
        EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
        environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }

    //添加ConfigurationPropertySource,如果存在则移动至列表最前面
    ConfigurationPropertySources.attach(environment);
    return environment;
} 

总结

run 启动初期使用 DefaultBootstrapContext 用于一些简单的对象注册,直到 ApplicationContext 可用之前。

在此期间将 JVM环境变量、系统环境变量、commandLine 命令行参数、profile 配置等信息加载至环境中,其次通过广播 ApplicationEnvironmentPreparedEvent 事件执行监听器的相关环境准备操作,其中就包括加载配置文件信息至环境中。

 

后续

下一章将讲解 application.yml 的加载过程。

标签:environment,run,SpringBoot,bootstrapContext,listener,listeners,源码,context,event
From: https://www.cnblogs.com/anlizhaomi/p/16975709.html

相关文章