springboot启动流程源码解析(带流程图)
大致流程如下:
- 初始化SpringApplication,从META-INF下的spring.factories读取
- ApplicationListener/ApplicationContextInitializer
- 运行SpringApplication的run方法
- 读取项目中环境变量、jvm配置信息、配置文件信息等
- 创建Spring容器对象(ApplicationContext)
- 利用ApplicationContextInitializer初始化Spring容器对象,读取启动类
- 调用spring的refresh加载IOC容器、自动配置类,并创建bean、servlet容器等信息
- springboot会调用很多监听器
- 如果启动时发生异常,则发送ApplicationFailedEvent事件
下面对上述流程进行源码细化分析,首先调用SpringApplication.run启动springboot应用:
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class) @EnableTransactionManagement(proxyTargetClass = true) @Import(MyDynamicDataSourceConfig.class) public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } }
进入run方法后,会进行SpringApplication进行启动,分两大步,第一步初始化SpringApplication,第二步调用run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
第一步初始化SpringApplication,new SpringApplication(primarySources)的流程如下(具体方法含义参考注释):
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 根据classpath下的类,推算当前web应用类型(REACTIVE/SERVLET/NONE,我们的项目一般是SERVLET) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 获取BootstrapRegistryInitializer对象,从META-INF/spring.factories中读取key为BootstrapRegistryInitializer,并实例化出对象 // BootstrapRegistryInitializer的作用是可以初始化BootstrapRegistry this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); //去spring.factories中去获取所有key:org.springframework.context.ApplicationContextInitializer为了初始化Spring容器ApplicationContext对象(可以利用 //ApplicationContextInitializer向Spring容器中添加ApplicationListener) setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //去spring.factories中去获取所有key: org.springframework.context.ApplicationListener,ApplicationListener是Spring中的监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //推测main()方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); }
第二步调用run方法,初始化完SpringApplication开始运行run方法,源码如下:
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime();//记录时间 //创建DefaultBootstrapContext对象,利用BootstrapRegistryInitializer初始化DefaultBootstrapContext对象 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; // 开启了Headless模式: configureHeadlessProperty(); //获取SpringApplicationRunListeners, //SpringBoot提供了一个EventPublishingRunListener,它实现了SpringApplicationRunListener接口 //spring会利用这个类,发布一个ApplicationContextInitializedEvent事件,可以通过定义ApplicationListener来消费这个事件 SpringApplicationRunListeners listeners = getRunListeners(args); // 发布ApplicationStartingEvent事件,在运行开始时发送 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 根据命令行参数 实例化一个ApplicationArguments ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 预初始化环境(见下面的源码分析): 读取环境变量(操作系统的环境变量/JVM的环境变量),读取配置文件信息(基于监听器,会利用EventPublishingRunListener发布一个ApplicationEnvironmentPreparedEvent事件, //默认会有一个EnvironmentPostProcessorApplicationListener来处理这个事件,当然也可以通过自定义ApplicationListener来处理这个事件,当ApplicationListener接收到这个事件之后,就会解析application.properties、application.yml文件, //并添加到Environment中) ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment);// 打印Banner //据webApplicationType创建不同的Spring上下文容器(有三种) context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //预初始化spring上下文,见下面的源码分析 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //刷新Spring容器,这一步中创建并初始化bean,创建并启动tomacat等(以tomcat为例,调到ServletWebServerApplicationContext的createWebServer()方法 //最后执行TomcatServletWebServerFactory的getWebServer方法) refreshContext(context); afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } //发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件 listeners.started(context, timeTakenToStartup); // 获取Spring容器中的ApplicationRunner/CommandLineRunner类型的Bean,并执行run方法 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners);//发布ApplicationFailedEvent事件 throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); //发布ApplicationReadyEvent事件和AvailabilityChangeEvent事件 listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null);//发布ApplicationFailedEvent事件 throw new IllegalStateException(ex); } return context; }
预初始化环境,创建Environment对象源码解析:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 根据webApplicationType 创建Environment 创建就会读取: java环境变量和系统环境变量 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 将命令行参数读取环境变量中 configureEnvironment(environment, applicationArguments.getSourceArgs()); // 将@PropertieSource的配置信息 放在第一位,它的优先级是最低的 ConfigurationPropertySources.attach(environment); // 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件 listeners.environmentPrepared(bootstrapContext, environment); // 将所有spring.main 开头的配置信息绑定到SpringApplication中 DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment);//更新PropertySources return environment; }
预初始化spring上下文源码解析:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context);// 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法 listeners.contextPrepared(context);// 发布了ApplicationContextInitializedEvent bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 获取当前spring上下文beanFactory (负责创建bean) ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences); if (beanFactory instanceof DefaultListableBeanFactory) { //在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常 ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } } // 设置当前spring容器是不是要将所有的bean设置为懒加载 if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 读取主启动类 (因为后续要根据配置类解析配置的所有bean),将启动类作为配置类注册到Spring容器中 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context);//读取完配置类后发送ApplicationPreparedEvent,默认利用EventPublishingRunListener发布一个ApplicationPreparedEvent事件 }
refreshContext(context),初始化,启动tomcat,这里就是常说到的springbean生命周期相关的代码:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 这里启动了tomcat onRefresh(); // Check for listener beans and register them. registerListeners(); // 初始化非懒加载的bean,springboot的单例默认是懒加载的 // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); contextRefresh.end(); } } }
1),springbean在容器里的两种存在形式,一种是我们项目中使用到的javabean,一种BeanDefinition;
2),BeanDefinition存在DefaultListableBeanFactory,javabean存在DefaultSingletonBeanRegistry的三级缓存中,javabean是通过BeanDefinition反射而来。
3),因为springboot默认是单例,单例又默认懒加载,所以如果你的bean不是项目启动就被调用,如在ApplicationRunner中,那么就可能项目启动成功,但是后续报错循环依赖,这种错误很可能发生在你在一个类中注入了另外一个类的实例,只是调了它一个简单的方法,简单到你自信不用单测直接可以上测试或者生产环境。
原文链接:https://blog.csdn.net/zkr1234562/article/details/128053698
标签:
spring
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)