Spring Boot(二)SpringBoot是如何启动Spring容器源码
SpringApplication run
调用SpringApplication.run启动springboot应用
1 SpringApplication.run(Application.class, args);
2:使用自定义SpringApplication进行启动
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");
//将启动类放入primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 根据main方法推算出mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
org.springframework.context.ApplicationContextInitializer
org.springframework.context.ApplicationListener
总结:1. 获取启动类:根据启动类加载ioc容器2.获取web应用类型
3.spring.factories读取了对外扩展的ApplicationContextInitializer ,ApplicationListener 对外扩展, 对类解耦(比如全局配置文件、热部署插件)
4. 根据main推算出所在的类
就是去初始化了一些信息
SpringBoot 事件监听器发布顺序
1.ApplicationStartingEvent在运行开始时发送,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送。
2.在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent。
3.准备ApplicationContext并调用ApplicationContextInitializers之后,将发送ApplicationContextInitializedEvent。
4.读取完配置类后发送ApplicationPreparedEvent。
5.在刷新上下文之后但在调用任何应用程序和命令行运行程序之前,将发送ApplicationStartedEvent。
6.紧随其后发送带有LivenessState.CORRECT的AvailabilityChangeEvent,以指示该应用程序被视为处于活动状态。
7.在调用任何应用程序和命令行运行程序之后,将发送ApplicationReadyEvent。
8.紧随其后发送ReadabilityState.ACCEPTING_TRAFFIC的AvailabilityChangeEvent,以指示应用程序已准备就绪,可以处理请求。
如果启动时发生异常,则发送ApplicationFailedEvent。
1.ApplicationStartingEvent
2.ApplicationEnvironmentPreparedEvent
run方法
这个方法是启动springboot最核心的逻辑
public ConfigurableApplicationContext run(String... args) {
// 用来记录当前springboot启动耗时
StopWatch stopWatch = new StopWatch();
// 就是记录了启动开始时间
stopWatch.start();
// 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 开启了Headless模式:
configureHeadlessProperty();
// 去spring.factroies中读取了SpringApplicationRunListener 的组件, 就是用来发布事件或者运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布1.ApplicationStartingEvent事件,在运行开始时发送
listeners.starting();
try {
// 根据命令行参数 实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 忽略beaninfo的bean
configureIgnoreBeanInfo(environment);
// 打印Banner 横幅
Banner printedBanner = printBanner(environment);
// 根据webApplicationType创建Spring上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//预初始化spring上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 加载spring ioc 容器 相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
// 加载自动配置类:invokeBeanFactoryPostProcessors , 创建servlet容器onRefresh
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
准备环境 prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据webApplicationType 创建Environment 创建就会读取: java环境变量和系统环境变量
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
ConfigurationPropertySources.attach(environment);
// 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件
listeners.environmentPrepared(environment);
// 将所有spring.main 开头的配置信息绑定SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//更新PropertySources
ConfigurationPropertySources.attach(environment);
return environment;
}
预初始化上下文 prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
applyInitializers(context);
// 发布了ApplicationContextInitializedEvent
listeners.contextPrepared(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);
}
// 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
// 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 设置当前spring容器是不是要将所有的bean设置为懒加载
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类,将它注册为BD、就像我们以前register(启动类);一个意思 (因为后续要根据配置类解析配置的所有bean)
load(context, sources.toArray(new Object[0]));
//4.读取完配置类后发送ApplicationPreparedEvent。
listeners.contextLoaded(context);
}
刷新 调用ServletWebServerApplicationContext refresh 之后就会调用AbstractApplicationContext的refresh方法
private void refreshContext(ConfigurableApplicationContext context) {
refresh((ApplicationContext) context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
总结: 1. 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
2.运行run方法
3.读取 环境变量 配置信息…
4. 创建springApplication上下文:ServletWebServerApplicationContext
5. 预初始化上下文 : 读取启动类
6.调用refresh 加载ioc容器
加载所有的自动配置类
创建servlet容器
在这个过程中springboot会调用很多监听器对外进行扩展
相关文章
- Spring DI - 依赖注入
- 《SpringBoot揭秘:快速构建微服务体系》—第2章2.1节Spring框架的起源
- Spring: Springboot 静态资源
- SpringMvc学习-5-Spring MVC 文件上传
- Spring+SpringBoot+SpringCloud 全攻略
- 691 页,41 个版块!终于有阿里大佬可以带我吃透 Spring 源码了
- 阿里内部最新教材:Spring+SpringBoot+SpringCloud 全家桶第五版
- 贼全! 一举通关的 Spring+SpringBoot+SpringCloud 全攻略, 是真香啊
- Drools整合spring,springboot
- Spring Boot-intellij idea导入方式搭建SpringBoot
- Spring Boot-Error:(3, 32) java: 程序包org.springframework.boot不存在
- 注意:Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...
- springboot(三):Spring boot中Redis的使用
- Spring cloud Zipkin 链路追踪安装配置和使用,SpringCloud Zipkin server 下载安装
- spring框架漏洞整理(Spring Boot Actuator命令执行漏洞)
- spring框架漏洞整理(Spring Framework漏洞)
- spring框架漏洞整理(Spring Data漏洞)
- spring框架漏洞整理(Spring Boot Actuator相关漏洞)
- SpringBoot入门三(java代码方式配置之使用@ConfigurationProperties实现Spring Boot配置文件配置项读取)
- SpringBoot入门一(能够用spring boot搭建项目)
- springBoot项目配置错误问题-@EnableWebMvc注解问题导致SpringBoot默认配置失效
- 【spring-bean】浅谈Spring的依赖注入(基础)
- 玩转spring boot——properties配置
- 【springBoot】SpringBoot修改启动logo图案
- spring-基于SpringBoot使用AOP技术实现操作日志管理
- spring-基于SpringBoot使用AOP技术实现操作日志管理