【java】(二) SpringBoot 源码解析——run启动加载过程——环境准备
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 的加载过程。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!