02-Spring Boot启动原理核心源码剖析
为什么springboot的jar包可以直接运行
1:
要让springboot的jar包可以直接运行首先我们需要引入一个插件,spring‐boot‐maven‐plugin
这个插件会帮我编写一个在manifest.mf的文件,这个文件主要作用就是
2.a:
把依赖的jar包导入BOOT-INF/lib目录下,也就是说这个jar里面还包含了很多的jar包,我们把这种称为fat jar。但是Java没有提供任何标准的方式来加载嵌套的jar文件,所以这就需要用到main-class指定的JarLauncher来加载这些jar包中的嵌套的jar。还可以加载jar包中的class。
2.b:
这个文件中还定义了start-class,这个start-class就是springboot启动类的路径。
3:
那么在我们运行java -jar的时候,java -jar会去找jar中的manifest文件,在那里面找到真正的启动类;通过反射去调用。
4:
Spring Boot应用打包之后,生成一个Fat jar,包含了应用依赖的jar包和Spring Boot loader相关的类。
5:
Fat jar的启动Main函数是JarLauncher,它负责创建一个LaunchedURLClassLoader来加载/lib下面的jar,并以一个新线程启动应用的Main函数。
6:
SpringBoot通过扩展URLClassLoader–LauncherURLClassLoader,实现了jar in jar中class文件的加载。
==============================================================================================
SpringBoot是如何启动Spring容器的
1. 获取启动类
// 将启动类放入primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
2.获取web应用类型
// 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
3.读取了对外扩展的ApplicationContextInitializer ,ApplicationListener
// 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
4. 根据main推算出所在的类
// 根据main方法推算出mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
就是去初始化了一些信息
然后就是调用run方法。
5:记录启动开始时间
stopWatch.start();
6:发布ApplicationStartingEvent事件
listeners.starting();
7:预初始化环境: 读取环境变量,通过监听器读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
8:创建Spring上下文
// 根据webApplicationType创建Spring上下文
context = createApplicationContext();
9://预初始化spring上下文
将配置类读取成bean定义
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
10:调用 spring的refresh方法;
在refresh方法中调用invokeBeanFactoryPostProcess方法,解析加了注解了的配置类,将类扫描为bean定义。
然后还会调用finishBeanFactoryInitizlization方法,会调用beanFactory将bean定义加载为完整的bean。
refreshContext(context);
==============================================================================================
SpringBoot是如何启动内置Tomcat的
1:
在run方法中的onrefresh方法中会创建一个webserver
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
2:
在这个createWebServer方法中,首先会判断servletContext是否存在
if (webServer == null && servletContext == null)
3:
如果不存在就会调用getWebServerFactory获取factory,然后调用getWebServer方法获取webServer
ServletWebServerFactory factory = this.getWebServerFactory();
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
4:
在getWebServer方法中就会new一个Tomcat
Tomcat tomcat = new Tomcat();
5:
然后就会向Tomcat的service中addConnector,Tomcat中setConnector,getEngine,
tomcat.getService().addConnector(connector);
tomcat.setConnector(connector);
this.configureEngine(tomcat.getEngine());
6:
然后会调用geTomcattWebServer返回TomcatWebServer
return this.getTomcatWebServer(tomcat);
7:
在geTomcattWebServer方法中会启动Tomcat
this.tomcat.start();
8:
并且会调用 this.startDaemonAwaitThread();,然后调用await方法让Tomcat保证挂起。
public void run() {
TomcatWebServer.this.tomcat.getServer().await();
}
9:
然后会回调调用selfInitialize方法
private ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
10:
在这个方法中就会调用onStartup方法
beans.onStartup(servletContext);
这个方法中就会去注册servletContext,然后servletContext.addServlet。
this.register(description, servletContext);
==============================================================================================
外置Tomcat如何启动springboot
1:
在我们的应用程序打成war包后,然后放在Tomcat里面的webApp里面去。然后运行startup,就会解压war包,启动Tomcat。
2:
但是Tomcat不能启动main方法的。这就需要使用到spi机制。
3:
首先使用exclusions排除start-web中内置的Tomcat。
4:
然后利用spi机制,去META-INF/services 文件夹中找到ServletContainerInitializer的实现类,从而创建它的实例调用onstartUp。
==============================================================================================
什么是spi机制
为 Service Provider Interface(服务提供者接口),是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件
夹查找文件,自动加载文件里所定义的类。
当servlet容器启动时候就会去META-INF/services 文件夹中找到javax.servlet.ServletContainerInitializer, 这个文件里面肯定绑定一个ServletContainerInitializer. 当servlet容器启动时候就会去该文件中找到ServletContainerInitializer的实现类,从而创建它的实例调用onstartUp。
==============================================================================================
SpringBoot的启动主流程
通过实例化SpringApplication来启动的,启动过程主要做了以下几件事情:
配置属性、获取监听器,发布应用开始启动事件初始化输入参数、配置环境,输出banner、创建上下文、预处理上下文、刷新上下文、再刷新上下文、发布应用已经启动事件、发布应用启动完成事件。
在SpringBoot中启动tomcat的工作在刷新上下这一步。而tomcat的启动主要是实例化两个组件:Connector、Container,一个tomcat实例就是一个Server,一个Server包含多个Service,也就是多个应用程序,每个Service包含多个Connector和一个Container,而一个Container下又包含多个子容器。