Springboot 启动流程分析
2022-04-05 23:36:06 0 举报
springboot 源码解析
作者其他创作
大纲/内容
指定实例化对象所使用的构造器的入参类型
ServletContext servletContext = getServletContext()
setInitializers()
finishBeanFactoryInitialization(beanFactory);
得到所有排除的自动配置类集合
方法调用完后 environment
获取 ServletContext 上下文对象
创建一个 NIO 协议的 Connector 连接器对象,并添加到刚创建的 tomcat 中
font color=\"#323232\
此时已经将 application.yml 中的配置加载完成
初始化所有 ApplicationContextInitializer 类型的对象,并保存至 initializers List 集合中
回调 IoC 容器中所有 ApplicationRunner 和 CommandLineRunner 类型的启动器
for
return getTomcatWebServer(tomcat);
设置资源加载器
获得 tomcat 连接对象
返回排序后的 `type` 类型的实例对象
connector.setThrowOnFailure(true)
移除要排除的自动配置类
String factoryTypeName = factoryType.getName();
ServletWebServerApplicationContext # finishRefresh
Connector connector = new Connector(this.protocol)
loadBeanDefinitions(Set<ConfigurationClass> configurationModel)
new SpringApplication(primarySources)
this.autoConfigurationEntries.add(autoConfigurationEntry)
getSelfInitializer().onStartup(servletContext)
webServer == null && servletContext == null
this.mainApplicationClass = deduceMainApplicationClass();
servletContext != null
List<T> instances = new ArrayList<>(names.size());
将当前 `environment` 的 MutablePropertySources 封装成 SpringConfigurationPropertySources 添加到 MutablePropertySources 首部
对于 Spring 中的模块驱动注解的实现都是通过 @Import 注解来实现的,模块驱动注解通常需要结合 @Configuration 注解一起使用,因为需要先被当做一个配置类,然后解析到上面有 @Import 注解后则进行处理,对于 @Import 注解的值有三种情况:1、该 Class 对象实现了 ImportSelector 接口,调用它的 selectImports(..) 方法获取需要被处理的 Class 对象的名称,也就是可以将它们作为一个 Bean 被 Spring IoC 管理。2、该 Class 对象实现了 ImportBeanDefinitionRegistrar 接口,会调用它的 registerBeanDefinitions(..) 方法,自定义地往 BeanDefinitionRegistry 注册中心注册 BeanDefinition(Bean 的前身)。3、该 Class 对象是一个 @Configuration 配置类,会将这个类作为一个 Bean 被 Spring IoC 管理。这里的 @EnableAutoConfiguration 自动配置模块驱动注解,通过 @Import 导入 AutoConfigurationImportSelector 这个类(实现了 DeferredImportSelector 接口)来驱动 Spring Boot 的自动配置模块
将一些工具 Bean 设置到 Spring 应用上下文中,如 Bean 名称生成器,资源加载器,类型转换器,供使用
context = createApplicationContext()
为 Spring 应用上下文设置 Environment 环境,这个environment 就是在之前被创强配置好的,用 prepareEnvironment 方法返回的environment 覆盖 context 中的 environment
如果通过 `spring.boot.enableautoconfiguration` 配置关闭了自动配置功能
从 `META-INF/spring.factories` 找到所有的 {@link AutoConfigurationImportFilter} 对 `configurations` 进行过滤处理 * 例如 Spring Boot 中配置了 {@link org.springframework.boot.autoconfigure.condition.OnClassCondition} * 在这里提前过滤掉一些不满足条件的自动配置类,在 Spring 注入 Bean 的时候也会判断
完成所有 bean 的实例化工作
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args)
return context;
获取类加载器
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir(\"tomcat\")
获取异常报告器,通过类加载器从 `META-INF/spring.factories` 文件中获取 SpringBootExceptionReporter 类型的类名称,并进行实例化
this.deferredImportSelectorHandler.process();
启动 Tomcat
context.setEnvironment(environment);
true
此图只截取了部分
else if
用一个 List 存放接下来要实例化的
@Conditional 与以下注解组合使用@ConditionalOnBean 当spring容器中有某一个bean时使用@ConditionalOnClass 当判断当前类路径下有某一个类时使用@ConditionalOnMissingBean 当spring容器中没有某一个bean时才使用@ConditionalOnMissingClass 当当前类路径下没有某一个类的时候才使用@ConditionalOnProperty 当配置文件中有某一个key value的时候才使用.............
准备好当前应用 Environment 环境,这里会加载出所有的配置信息,包括 `application.yaml` 和外部的属性配置,就是这一步解析了 application.yml 配置,并封装到了 environment 对象中
对所有 SpringApplicationRunListener 进行广播,发布 ApplicationContextInitializedEvent 初始化事件,这个 listeners 就是执行 run 方法的第一步,getRunListeners 方法返回的
ConfigFileApplicationListener 注释
ClassLoader classLoader = getClassLoader();
为上一步获取到的所有类名称创建对应的实例对象
该注解很简单,上面标注了 @Configuration 元注解,所以作用相同,同样是将一个类标注为配置类,能够作为一个 Bean 被 Spring IoC 容器管理
font color=\"#f44336\
对 Connector 进行配置,设置 server.port 端口、编码server.tomcat.min-spare-threads 最小空闲线程 和 server.tomcat.accept-count 最大线程数
创建一个 Tomcat 对象 tomcat
从 `META-INF/spring.factories` 找到所有的 {@link AutoConfigurationImportListener} 事件监听器 * 触发每个监听器去处理 {@link AutoConfigurationImportEvent} 事件,该事件中包含了 `configurations` 和 `exclusions` * Spring Boot 中配置了一个 {@link org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener} * 目的就是将 `configurations` 和 `exclusions` 保存至 {@link AutoConfigurationImportEvent} 对象中,并注册到 IoC 容器中,名称为 `autoConfigurationReport`
这个 refresh 方法在笔者另一篇的 《Spring 源码解析》 中详细分析过,此处不做细致分析,只对重要步骤做解析
@SpringBootConfiguration
获取需要排除的自动配置类 可通过 `@EnableAutoConfiguration` 注解的 `exclude` 和 `excludeName` 配置 也可以通过 `spring.autoconfigure.exclude` 配置
通过类加载器从 `META-INF/spring.factories` 文件中获取 SpringApplicationRunListener 类型的类名称,并进行实例化
this.tomcat.start()
向上引用,实际类型是 org.springframework.beans.factory.support.DefaultListableBeanFactory
......
返回 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext,至此,springBoot 启动完成
获取 `primarySources`(例如你的启动类)和 `sources`(例如 Spring Cloud 中的 @BootstrapConfiguration)源对象
这个类非常重要,AutoConfigurationImportSelector 此时已经被实例化了
this.group.selectImports()
beanFactory.registerSingleton(\"springApplicationArguments\
String name : names
再次进行上面的处理过程,防止上面几步对上面的 PropertySources 有修改
this.deferredImportSelectorHandler = DeferredImportSelectorHandler
this.resourceLoader = resourceLoader
处理主启动类上导入的 AutoConfigurationImportSelector 类
run(String... args)
红框就是 springboot 会在哪些位置加载 application.yml or application.properties
Set<String> processedConfigurations = this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
设置主要的 Class 类对象,通常是我们的启动类
将所有的自动配置类和要排除的自动配置类封装成一个 AutoConfigurationEntry 对象,并返回
对所有的 SpringApplicationRunListener 监听器进行广播,发布 ApplicationReadyEvent 应用已就绪事件
parser.getConfigurationClasses() 拿到包括开发者自定义的自动配置类在内的所有需要自动配置的类的 Set<ConfigurationClass>集合
返回值 factoryTypeName 是 org.springframework.boot.autoconfigure.EnableAutoConfiguration,作为键从 spring.factories 文件中找到所有自动配置
TomcatWebServer # startWebServer
performDeferredLoadOnStartup();
AnnotationAwareOrderComparator.sort(instances)
finishRefresh()
ServletWebServerFactory factory = getWebServerFactory();
AutoConfigurationImportSelector # AutoConfigurationGroup# process
checkThatConnectorsHaveStarted();
对 Spring 应用上下文做一些初始化工作,并将主启动类封装成 BeanDefinition,放入 map,例如执行 ApplicationContextInitializer#initialize(..) 方法
return environment;
将 main 方法的参数 封装为单例 bean,注册进容器
扫描指定路径下的标有 @Component 注解的类,解析成 Bean 被 Spring IoC 容器管理
将上一步拿到的 set 集合作为参数传入,封装、注册所有需要的自动配置类到 beanDefinitionMap
listeners.environmentPrepared(environment);
源码阅读起点
通过类加载器从 `META-INF/spring.factories` 文件中获取类型为 `type` 的类名称,这里会加载出所有实现了 ApplicationContextInitializeListener 的实现类
getSpringFactoriesInstances(ApplicationContextInitializer.class)
environment.setConversionService((ConfigurableConversionService) conversionService);
!isEnabled(annotationMetadata
通过 `@Order` 注解进行排序
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
这个步骤在笔者另外一篇作品 《Spring 源码解析中详细解析过,此处不赘述》
primarySource 即 启动类的 Class 对象
根据用户配置,配置 environment 环境,包括本地环境变量,如 maven ,jdk 等信息
refresh(context)
(ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
return instances;
配置属性源里面的属性例如有默认属性则将他们添加至最后,命令行中的参数则添加至最前面
得到实例对象
通知 ApplicationContextInitializer 对 Spring 应用上下文进行初始化工作,ApplicationContextInitializer 是在 SpringApplication 的构造方法中初始化的, 参考 SpringApplication 构造方法
BeanUtils.instantiateClass(clazz);
listeners.running(context);
到这一步,循环上一步拿到的 set 集合,将集合里的自动配置类封装为 beanDefinition,存放进 beanDefinitionMap,即交由 spring 容器管理
这里详细解释 @SpringbootApplication 注解
利用 BeanUtils 实例化 AutoConfigurationImportSelector
刷新 Spring 应用上下文,在这里会完成所有 Spring Bean 的实例化,同时会初始化好 Servlet 容器,例如 Tomcat这一步涉及到 Spring IoC 的所有内容,包括 springboot的自动装配
从 `configurations` 中将 `exclusions` 需要排除的自动配置类移除
实例化 TomcatWebServer时 会将 DispatcherServlet 以及一些 filter 添加到 tomcat 中
关于条件装配
实例化 AutoConfigurationImportSelector
返回存放所有ApplicationContextInitializeListener 实现类对象的 list 集合
new PackageImport(metadata).getPackageName()
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
configurations.removeAll(exclusions);
禁止自动部署
Application # main
返回当前包名
ConfigurationPropertySources.attach(environment);
new ArrayList<>(new LinkedHashSet<>(list));
获取当前 WebServer 容器对象,首次进来为空
这个方法专门处理 @Import 注解 导入的类,在 《Spring 源码解析》 有详细解释,就是在这一步,会对主启动类做处理,因为主启动类上有 @SpringApplication 这个组合注解,详细请看箭头
从 factory 工厂中创建一个 WebServer 容器对象 例如创建一个 TomcatWebServer 容器对象,并初始化 ServletContext 上下文,创建 Tomcat 容器并启动 启动过程异步触发了 org.springframework.boot.web.embedded.tomcat.TomcatStarter#onStartup 方法 也就会调用这个传入的 ServletContextInitializer 的 selfInitialize(ServletContext) 方法
根据类的全限定名创建CLass 对象
获取 @EnableAutoConfiguration`注解的配置信息 exclude excludeName
返回准备好的 `environment`
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).font color=\"#f44336\
initialize()
对标注有 @Order 注解的自动配置类进行排序
得到经过过滤后所有符合条件的自动配置类集合
执行 Spring 应用上下文初始器 例如 ContextIdApplicationContextInitializer 会向 Spring 应用上下文注册一个 ContextId 对象
Set<String> allExclusions = this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
factoryClassName = interface org.springframework.context.ApplicationContextInitializer
去重 + 排除
检查 Connector 是否都已经启动
如果不是自定义的 Environment 则需要根据 Web 应用类型转换成对应 Environment 类型
调用 getRunListeners(..) 方法,初始化所有 SpringApplicationRunListener 类型的对象,并全部封装到 SpringApplicationRunListeners 对象中,为初始化环境 environment 做准备
创建一个应用参数对象,将 `main(String[] args)` 方法的 `args` 参数封装起来,便于后续使用
postProcessApplicationContext(context);
启动所有的 SpringApplicationRunListener 监听器,例如 EventPublishingRunListener 会广播 ApplicationEvent 应用正在启动的事件,它里面封装了所有的 ApplicationListener 对象,那么此时就可以通过它们做一些初始化工作,进行拓展
Registrar # registerBeanDefinitions
打印 banner 内容
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
@ComponentScan
WebServer webServer = this.webServer
创建应用上下文环境,context 也是 BeanFctory 的一个实现类例如 Servlet(默认)会创建一个 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 实例对象简单理解,就是 根据当前 classpth 下存在的类判断当前应用环境,然后利用BeanUtils 反射创建了一个包括了 beanFactory(DefaultListableBeanFactory) 应用上下文,应用上下文可以看做是当前存放应用所需一系列属性的集合
这 7 个监听器就是在 SpringApplication 构造方法中扫描并实例化的,其中,就是由 ConfigFileApplicationListener 对 application.yml 配置文件进行实例化的
这一步,只是收集需要的自动配置类,这些类还只是字符串(类的全限定名)并没有将其封装为 BeanDefinition 存入 beanDefinitionMap
Tomcat tomcat = new Tomcat();
.....
this.beanFactory = new DefaultListableBeanFactory();
配置属性源里面的属性。例如有默认属性则将他们添加至最后,命令行中的参数则添加至最前面
对所有的 SpringApplicationRunListener 广播 ApplicationPreparedEvent 应用已准备事件会把 ApplicationListener 添加至 Spring 应用上下文中
这个方法非常重要,springboot 扫描 META-INF/spring.factories 都是通过这个方法扫描,并将扫描到的类进行实例化
激活对应的配置文件,如application-dev.ymlapplication-test.ymlapplication-prod.yml
listeners.started(context);
这一步跟上一步是一样的,过程见上一步,只是初始化的监听器类型变为,ApplicationListener,存放集合见右
refreshContext(context);
return instances;
ConversionService conversionService = ApplicationConversionService.getSharedInstance()
返回结果
@EnableAutoConfiguration
TomcatServletWebServerFactory #getWebServer(ServletContextInitializer... initializers)
通过工厂获取 servlet 容器
applyInitializers(context);
tomcat.getHost().setAutoDeploy(false);
WebServer webServer = startWebServer()
将 `environment` 绑定到当前 SpringApplication 上
利用 LinkedHashSet 对所有的自动配置类进行去重,因为 jar 包可能有重复,得到多个同样的自动配置类
AutoConfigurationEntry 的 configurations 属性是 List<String>,存放从 /META-INF/spring.factories 中扫描到的所需要的自动装配类的全限定名
listener.starting();
invokeBeanFactoryPostProcessors(beanFactory)
那么这里主动调用 this#selfInitialize(ServletContext) 方法来注册各种 Servlet、Filter
AnnotationAttributes attributes = getAttributes(annotationMetadata);
configurations = removeDuplicates(configurations);
clazz = org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
this.group.font color=\"#f44336\
processedConfigurations.removeAll(allExclusions);
listeners.starting();
参数:getSpringFactoriesLoaderFactoryClass()
对所有的 SpringApplicationRunListener 监听器进行广播,发布 ApplicationStartedEvent 应用已启动事件
初始化所有 ApplicationListener 类型的对象,并保存至 listeners 集合中,例如 ConfigFileApplicationListener 和 LoggingApplicationListener
对所有的 SpringApplicationRunListener 广播 ApplicationEvent 应用环境已准备好的事件,这一步比较复杂,例如 Spring Cloud 的 BootstrapApplicationListener 监听到该事件会创建一个 ApplicationContext 作为当前 Spring 应用上下文的父容器,同时会读取 bootstrap.yml 文件的信息这里会有一个 ConfigFileApplicationListener 监听到该事件然后去解析 application.yml 等应用配置文件的配置信息,这个 ConfigFileApplicationListener 就是在 SpringApplication 构造方法中扫描 ApplicationListener 接口实现类时,扫描并实例化的重点记住,这个方法是通过 ConfigFileApplicationListener 加载 application.yml 中的配置,并封装到environment 对象中的
@Import(AutoConfigurationPackages.Registrar.class)
获取嵌入式 Servlet 容器工厂对象 默认为 TomcatServletWebServerFactory
this.webApplicationType = WebApplicationType.deduceFromClasspath()
parser.parse(candidates)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
Set<Object> sources = getAllSources();
将这个目录作为 Tomcat 的目录
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(font color=\"#f44336\
根据 Web 应用的类型创建一个 StandardEnvironment 对象 `environment`
SpringApplicationRunListener listener : this.listeners
customizeConnector(connector)
扫描解析所有需要交给 spring 管理的对象,封装为 BeanDefinition
instantiateClass(clazz.getDeclaredConstructor());
将 loadStartUp>0 的servlet 启动起来
从所有 jar 包中的 `META-INF/spring.factories` 文件中找到 `@EnableAutoConfiguration` 注解对应的类(需要自动配置的类)
tomcat.getService().addConnector(connector)
完成刷新 Spring 应用上下文的后置操作,空方法,留给开发者自己做扩展
grouping.getImports()
处理 `exclusions` 中特殊的类名称,保证能够排除它
loadFactoryNames
tomcat.setBaseDir(baseDir.getAbsolutePath());
创建一个 TomcatEmbeddedContext 上下文对象,并进行初始化工作,配置 TomcatStarter 作为启动器 会将这个上下文对象设置到当前 tomcat 中去
this.initializers.addAll(initializers)
ConfigurationPropertySources.attach(environment)
扫描当前主启动类所在包下的所有要交给 spring 管理的类,封装注册进 beanDefinitionMap,这一步最重要的就是实例化了 AutoConfigurationImportSelector,但这个类并不会交给 spring 管理
GenericApplicationContext# 构造方法
Connector connector = this.tomcat.getConnector();
((AbstractApplicationContext) applicationContext).refresh()
return EMPTY_ENTRY
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes)
getCandidateConfigurations,Candidate 译作“候选”,表示当前得到的所有自动装配类,还只是候选,不是所有的自动装配类都会被spring 加载到内存并管理
启动在 tomcat 启动时需要自动启动的 servlet(如 DispatcherServlet)
根据调用栈获取当前 main 方法类名,即主启动类的全限定名
得到 自动配置类放入 AutoConfigurationEntry 对象中
ServletWebServerApplicationContext #createWebServer();
this.reader.loadBeanDefinitions(configClasses);
bindToSpringApplication(environment)
!this.isCustomEnvironment
@AutoConfigurationPackage
将实例对象放到 第一步 new 好的 对象中
ConfigurationClass configClass : configurationModel
this.webServer = factory.getWebServer(getSelfInitializer())
实例化 IOC 容器,之后会放入 context 应用环境上下文
AutoConfigurationImportSelector
tomcat.setConnector(connector)
Banner printedBanner = printBanner(environment);
listeners.contextLoaded(context)
listener.environmentPrepared(environment);
configureEngine(tomcat.getEngine());
handler.processGroupImports()
AutoConfigurationImportSelector # AutoConfigurationGroup #selectImports
注册一个 BasePackages 类的 BeanDefinition,角色为内部角色,名称为 `org.springframework.boot.autoconfigure.AutoConfigurationPackages`
获取 ConversionService 类型转换器并设置到 Environment 对象中
反射获取构造方法
创建一个临时目录(退出时删除)
通过 classpath 判断是否存在相应的 Class 类对象,来推断当前 应用的类型(REACTIVE、SERVLET、NONE),默认为 SERVLET,不同的类型后续创建的 Environment 类型不同
SpringApplicationRunListeners listeners = getRunListeners(args);
processConfigBeanDefinitions(registry)
if
instances.add(instance);
@Import(AutoConfigurationImportSelector.class)
String factoryClassName = factoryClass.getName()
如果 WebServer 和 ServletContext 都为空,则需要创建一个 使用 Spring Boot 内嵌 Tomcat 容器则会进入该分支
onRefresh();
ConfigurableEnvironment environment = getOrCreateEnvironment();
listeners.contextPrepared(context);
否则,如果 ServletContext 不为空,说明使用了外部的 Servlet 容器(例如 Tomcat)
@SpringbootApplication
0 条评论
下一页