SpringBoot启动原理

背景java

1> 你们都知道SpringBoot是经过main函数启动的,这里面跟踪代码处处都没有找到while(true),为何启动后能够一直跑?程序员

2> SpringBoot默认使用tomcat做为web容器。你们也能够经过在pom文件中exclusion掉tomcat,denpendency jetty 的方法来使用jetty。那SpringBoot是怎么作到在不一样web容器之间切换的呢?web

3> 传统的web容器好比jetty本质上是直接经过java start.jar 来启动,以后来加载spring上下文的,SpringBoot经过main函数是怎么来启动web容器的呢?spring

本文就这三个问题展开论述。apache

 

问题1分析tomcat

问题1很简单,启动后一直跑是由于启动了线程池。原理就是有非deamon的线程在跑。Java虚拟机规范定义要等全部用户线程都运行完才会退出。网络

因此这个原理就和下面启动线程池同样架构

程序员修炼之道教咱们:不要假定,要证实。虽然jetty使用线程池是常识,咱们也来跟踪下源码,看看线程池是在哪里初始化的:eclipse

 

org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory类里,建立Server的使用使用线程池做为初始化参数。而后建立了socket链接来监听端口。(对于socket链接有以前没接触过的,能够本身查一下。建议动手实践。《Java异常处理总结》这篇文章里有不错的简单小例子能够实操下。)socket

 

到这里,你们应该都明白了为何启动后一直不停。可是又有疑问了:JettyServletWebServerFactory是个什么东东?

 

问题2分析

 

关于问题2,咱们写个最简单的类来debug一下:

 

 

 

进入SpringAppication.run的源码能够看到,里面建立了一个context,默认是AnnotationConfigServletWebServerApplicationContext。一初始化,在Bean定义里就加载了spring开天辟地的5个Bean。

 

继续向下执行走到AbstractApplicationContext的refresh方法,执行到onRefresh时,你进入方法里发现实际上执行的是

ServletWebServerApplicationContext的onFresh

 

这里面实际只作了一件事:建立web服务。

 

进入这个方法,debug到getWebServerFactory

来看一下:

获取的正式JettyServletWebServerFactory。为啥不是TomcatServlet呢?ServletWebServerFactoryAutoConfiguration的源码很好的说明了这个问题。源码的大意是当tomcat依赖存在就用tomcat,否则就按顺序找jetty存不存在,不存在再找Undertow存不存在。找到了就返回这个bean做为Servlet的工厂类。

@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
public ServletWebServerFactoryAutoConfiguration() {
}

@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}

@Bean
@ConditionalOnClass(
name = {"org.apache.catalina.startup.Tomcat"}
)
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}

public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public BeanPostProcessorsRegistrar() {
}

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
}

}

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory != null) {
this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
}
}

private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}

}
}
}

至此第二个问题也真相大白。

 

问题3分析

第三个问题是传统的web容器好比jetty本质上是直接经过java start.jar 来启动,以后来加载spring上下文的,SpringBoot经过main函数是怎么来启动web容器。

这个问题在前面问题分析过程当中也给了不少线索。咱们来回顾下:SpringApplication.run里会建立Spring的应用上下文,默认是AnnotationConfigServletWebServerApplicationContext。首先会加载Spring开天辟地的5个Bean。而后它初始化各类Bean工厂。

SpringBoot在ServletWebServerApplicationContext中重载了onRefresh方法,除了之前Spring默认的onRefresh方法外还增长了createWebServer方法,在这个方法中对Web容器进行了初始化工做。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
</exclusion>
</exclusions>
</dependency>

由于选择servlet容器是相似于使用基于条件的注解方式。由于当exclusion掉tomcat后,只有jetty知足条件,因此会加载JettyServletWebServerFactory。

经过getWebServer方法会new一个WebServer对象,new对象的方法会调用initialize方法,在这个方法中会对容器进行初始化并启动。

而容器启动的基本原理就是建立个线程池和网络套接字。用线程去处理套接字读写的内容。

 

总结

文本用带有少量说明的三个问题开场展开论述,实际是使用了麦肯锡大法中的SCQA架构。

SCQA架构是金字塔模型里面突出的一个论述方法,即“情境(Situation)、冲突(Complication)、问题(Question)、答案(Answer)”。能够帮助咱们在陈述事实时条理更为清晰、有效。

SCQA其实只是麦肯锡作了总结。这个方法李清照都在用:

昨夜雨疏风骤,浓睡不消残酒 (情境)

试问卷帘人,却道海棠依旧(冲突)

知否,知否(问题)

应是绿肥红瘦(答案)

 

文章正文看似一步步回答问题,实际上在讲述怎样去看spring源码,了解spring原理的一个过程。即:带着问题去看,debug跟踪源码验证 的方法。

相关文章
相关标签/搜索