这篇文章主要介绍了SpringBoot启动原理的分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
主启动类方法:
@SpringBootApplication
public class MyJavaTestApplication {
public static void main(String[] args) {
SpringApplication.run(MyJavaTestApplication.class, args);
}
}
点击进入方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
先看看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));
//判断当前web容器的类型,一般返回SERVLET,标识是web类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取META-INF/spring.factories文件中以//org.springframework.boot.Bootstrapper和
//org.springframework.boot.BootstrapRegistryInitializer为key的class
//创建对象,然后装入对象属性中;
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
setInitializers((Collection)
//获取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationContextInitializer的key的对
//存入到initializers集合属性中;
getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationListener的key的对
//存入到listeners集合属性中;
setListeners((Collection)
getSpringFactoriesInstances(ApplicationListener.class));
//找到main方法的启动主类class对象赋值到对象属性中。
this.mainApplicationClass = deduceMainApplicationClass();
}
spring.factories读取了对外扩展的ApplicationContextInitializer ,ApplicationListener 对外扩展, 对类解耦(比如全局配置文件、热部署插件)
所以我们可以利用这一特性,在容器加载的各个阶段进行扩展。
上面读取到的对象getSpringFactoriesInstances(ApplicationContextInitializer.class))
存入到initializers集合中的对象
getSpringFactoriesInstances(ApplicationListener.class));
存入到listeners集合属性中的对象
再看一下最重要的run方法:
public ConfigurableApplicationContext run(String... args) {
// 用来记录当前springboot启动耗时
StopWatch stopWatch = new StopWatch();
// 就是记录了启动开始时间
stopWatch.start();
//创建DefaultBootstrapContext bootstrapContext = new
//DefaultBootstrapContext();对象,然后循环执行上面文件中赋值的bootstrapRegistryInitializers集合中对象的方法,入参就是bootstrapContext对象,利用此处我们可以扩展改变bootstrapContext对象中的一项属性值等,在这里还不知道此bootstrapContext对象的用处。
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现
ConfigurableApplicationContext context = null;
// 开启了Headless模式,暂时不知道此模式的作用
configureHeadlessProperty();
//去spring.factroies中读取了 org.springframework.boot.SpringApplicationRunListener为key的对象,默认是EventPublishingRunListener对象,然后封装进SpringApplicationRunListeners 对象中,此对象还是比较有用的,用来发布springboot启动进行中的各个状态的事件,上面方法中读取到的监听器就可以监听到这些事件,所以可以运用这些特性进行自己的扩展。
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布1.ApplicationStartingEvent事件,在运行开始时发送 ,发送springboot启动开始事件;
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//根据启动项目命令所带的参数创建applicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//初始化环境变量:读取系统环境变量、发送了一个ApplicationEnvironmentPreparedEvent事件,利用相关监听器来解析项目中配置文件中的配置,(EnvironmentPostProcessorApplicationListener
监听器解析的配置文件)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 忽略beaninfo的bean
configureIgnoreBeanInfo(environment);
// 打印Banner 横幅
Banner printedBanner = printBanner(environment);
//根据webApplicationType 容器类型,创建对应的spring上下文,一般是AnnotationConfigServletWebServerApplicationContext;
context = createApplicationContext();
//给spring上下文赋值DefaultApplicationStartup对象
context.setApplicationStartup(this.applicationStartup);
//预初始化上下文,这里做了给上下文添加environment对象,上面获取到的initializers集合中的ApplicationContextInitializer对象执行其入参为上下文initialize方法,对上下文做编辑,所以此处我们可以做扩展;发送ApplicationContextInitializedEvent容器初始化事件;发送BootstrapContextClosedEvent事件;最重要的一个方法就是把启动配置类注册成了beanDefinition;发送ApplicationPreparedEvent事件,并把listeners集合属性中的事件添加到上下文中;
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新容器:和spring中的refresh()方法的作用是一样的,主要的作用就是读取所有的bean转成beanDefinition然后再创建bean对象;不过这个容器重写了其中的onRefresh()方法,在此方法中,创建了springboot内置的tomcat对象并进行启动;接下来特别说明一下这个内置tomcat
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
//打印启动时间;
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布容器启动事件ApplicationStartedEvent;发布AvailabilityChangeEvent容器可用实践;
listeners.started(context);
//执行容器中ApplicationRunner、ApplicationRunner类型对象的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
//发布ApplicationReadyEvent容器已经正常事件;
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, 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;
}
lprepareContext
l预初始化上下文
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);
}
1.初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
2.运行run方法
3.读取 环境变量 配置信息…
4.创建springApplication上下文:ServletWebServerApplicationContext
5.预初始化上下文 : 读取启动类
6.调用refresh 加载ioc容器
7.加载所有的自动配置类
8.创建servlet容器
9.ps.在这个过程中springboot会调用很多监听器对外进行扩展
看一下内置tomcat如何启动的:
Springboot的spring容器ServletWebServerApplicationContext对象重新了refresh()方法中的onRefresh()放用来启动tomcat
@Override
protected void onRefresh() {
super.onRefresh();
try {
//创建tomcat
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
createWebServer创建tomcat的方法(也可以是Jetty,根据配置)
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
//如果servletContext 为null说明是内置tomcat,不为null,则使用的是外置tomcat,这个servletContext 是tomcat创建传入的;
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
//获取servlet容器创建工厂,tomcat的是TomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
//创建内置tomcat对象,并启动;getSelfInitializer()这个方法也是很重要的,返回的是ServletWebServerApplicationContext#selfInitialize方法的引用函数,其作用是tomcat启动时会回调此方法,并传入servletContext对象,进行DispacherServlet添加到servletContext中,把当前spring容器和filter过滤器也添加到servletContext中
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
selfInitializ方法
private void selfInitialize(ServletContext servletContext) throws ServletException {
//把spring容器和servletContext进行相互引用
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//servletContext中添加Dispacherservlet和多个fileter
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
到此这篇关于Springboot启动原理详细讲解的文章就介绍到这了,更多相关Springboot启动原理内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
本文标题为:Springboot启动原理详细讲解
基础教程推荐
- Java实现查找文件和替换文件内容 2023-04-06
- springboot自定义starter方法及注解实例 2023-03-31
- Java数据结构之对象比较详解 2023-03-07
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- Java文件管理操作的知识点整理 2023-05-19
- Java并发编程进阶之线程控制篇 2023-03-07
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- java实现多人聊天系统 2023-05-19
- Java实现线程插队的示例代码 2022-09-03
- java基础知识之FileInputStream流的使用 2023-08-11