SpringBoot(三)启动流程分析
springboot启动过程中会用到事件发布机制。这里先事件发布说明一下。
Spring中的事件发布机制。网上有一篇文章介绍的很详细,可以参考:https://www.cnblogs.com/takumicx/p/9972461.html
但是在SpringBoot中庸的是事件驱动机制,和Spring中的机制有一些区别。
先来从图上整体看下springboot的启动流程。
springboot的监听机制加载执行过程如下:
1:在SpringApplication.run方法中两步:
// 加载监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 执行 listeners.starting(bootstrapContext, this.mainApplicationClass);
SpringApplicationRunListeners listeners = this.getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
// 从spring.factories中加载key为 SpringApplicationRunListener.class对应的value值 并封装成返回的这个类实例 注意这个spring.factoies是在spring-boot.jar里面的 return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }
加载的就一个 EventPublishingRunListener。
this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args) 这个方法不仅会加载spring.factories中的类,还会进行实例化。而EventPublishingRunListener 只有一个构造器。
public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args;
// 直接new了一个事件发布器 this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 这里取的就是 上面spring.factories中加载出来的ApplicationListener 监听器 Iterator var3 = application.getListeners().iterator(); while(var3.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var3.next();
/ /添加事件发布器中 方法是在AbstractApplicationEventMulticaster中 会先排除代理类再进行添加 this.initialMulticaster.addApplicationListener(listener); } }
然后通过 SpringApplicationRunListeners的构造器,把这个EventPublishingRunListener 赋值给了全局变量 listeners。
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) { this.log = log; this.listeners = new ArrayList(listeners); this.applicationStartup = applicationStartup; }
listeners.starting(bootstrapContext, this.mainApplicationClass);
发布事件
根据事件找到匹配的监听器,执行。异步执行就丢到线程池中执行。同步的话就直接调用。
2:在SpringApplication.run方法中下一步:准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
这里面又发布了一个环境准备的事件。
这里发布了一个 ApplicationEnvironmentPreparedEvent 事件,这个事件对应的监听器 ConfigFileApplicationListener.
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors(); postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); Iterator var3 = postProcessors.iterator(); while(var3.hasNext()) { EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
// 这里调用还是当前的这个监听器类 postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } }
List<EnvironmentPostProcessor> loadPostProcessors() {
// 加载一些环境处理的后置处理器 return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, this.getClass().getClassLoader()); }
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); 中最后会调用到加载配置文件的逻辑。
void load() { FilteredPropertySource.apply(this.environment, "defaultProperties", ConfigFileApplicationListener.LOAD_FILTERED_PROPERTY, (defaultProperties) -> { this.profiles = new LinkedList(); this.processedProfiles = new LinkedList(); this.activatedProfiles = false; this.loaded = new LinkedHashMap();
// 会激活配置文件 根据 spring.profiles.active this.initializeProfiles(); // 然后把文件读进来 while(!this.profiles.isEmpty()) { ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll(); if (this.isDefaultProfile(profile)) { this.addProfileToEnvironment(profile.getName()); } this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true)); this.addLoadedPropertySources(); this.applyActiveProfiles(defaultProperties); }); }
3:创建容器 context = this.createApplicationContext();
根据不同的类型,创建不同的容器
4:准备上下文 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 把environment设置到上下文里面
context.setEnvironment(environment); this.postProcessApplicationContext(context);
// 调用初始化的方法 this.applyInitializers(context);
// 发布一个ApplicationContextInitializedEvent 事件 listeners.contextPrepared(context); if (this.logStartupInfo) { this.logStartupInfo(context.getParent() == null); this.logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } Set<Object> sources = this.getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); this.load(context, sources.toArray(new Object[0]));
// 发布一个准备完成的事件, 在这一步 会把springboot 中的监听器都放入到spring容器context中 listeners.contextLoaded(context); }
this.applyInitializers(context);
protected void applyInitializers(ConfigurableApplicationContext context) {
// 这里获取调用的就是 SpringApplication构造函数执行时 加载的spring.factoies中 ApplicationContextInitializer for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
注意一点,如果是我们自定义的监听器,如果是监听的这些启动过程中的事件,需要把监听器放到spring.factories中或者yml文件中才会生效,如果是@Component放到spring 容器中是不会生效的,因为到这里spring容器还没创建好。
这一步把springboot中的监听器都加入到spring容器中,在这一步之后,springboot中的监听器就能监听spring容器中的事件了。
但是spring中的监听器监听不到springboot中的事件。
public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context)); }
5:刷新上下文 refreshContext(context);
6: afterRefresh(context, applicationArguments); 这是一个空方法,可以用于扩展,重写这个方法。
7: listeners.started(context); springboot启动后的事件,这个还没有对应的监听器,可以用于扩展,实现一个监听器监听这个事件做处理。
public void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); }
8: listeners.running(context); 也是可以扩展的
public void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); }
通过上面的流程,可以知道,springboot中事件发布器就是 EventPublishingRunListener,它里面定义了一系列事件发布的方法。然后靠 spring中的SimpleApplicationEventMulticaster 发布事件。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现