【Java】springboot源码解析-管中窥豹系列之web服务器(七)

springboot源码解析-管中窥豹系列之web服务器(七)

丰极发布于 今天 09:44

一、前言

  • Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去。
  • 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot源码管中窥豹系列。

【Java】springboot源码解析-管中窥豹系列之web服务器(七)

二、web服务器

  • 以前的的spring项目或者springmvc项目都需要一个web服务器,tomcat,或者其它的
  • 使用springboot之后,我们不再需要配置web服务器,因为springboot帮我们集成了
  • 今天我们来分析一下源码,看看在哪里实现的,知其然知其所以然

三、源码分析

  • 还是从SpringApplication的run方法开始看
  • 不熟悉的可以看之前的文章:springboot源码解析-管中窥豹系列之总体结构(一)

SpringApplication.java

public ConfigurableApplicationContext run(String... args) {

...

try {

...

refreshContext(context);

...

}

catch (Throwable ex) {

...

}

...

return context;

}

  • 接着进入到 refreshContext(context) 里面

AbstractApplicationContext.java

@Override

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

...

try {

...

// Initialize other special beans in specific context subclasses.

onRefresh();

...

}

catch (BeansException ex) {

...

}

finally {

...

}

}

}

  • 进入到 onRefresh() 方法

protected void onRefresh() throws BeansException {

// For subclasses: do nothing by default.

}

  • 注意这个是一个protected方法,我们进入到子实现里面
  • 具体用的哪个context,请看之前的文章:springboot源码解析-管中窥豹系列之项目类型(二)

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."

+ "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."

+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."

+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {

Class<?> contextClass = this.applicationContextClass;

if (contextClass == null) {

try {

switch (this.webApplicationType) {

case SERVLET:

contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);

break;

case REACTIVE:

contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);

break;

default:

contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);

}

}

catch (ClassNotFoundException ex) {

throw new IllegalStateException(

"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);

}

}

return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);

}

  • 最常用的就是普通web项目,我们看这一个
  • 我们到org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext里面找 onRefresh() 方法
  • 没找到,在父类ServletWebServerApplicationContext里面找到了

ServletWebServerApplicationContext.java

@Override

protected void onRefresh() {

super.onRefresh();

try {

createWebServer();

}

catch (Throwable ex) {

throw new ApplicationContextException("Unable to start web server", ex);

}

}

  • 我们到 createWebServer() 方法里面

private void createWebServer() {

WebServer webServer = this.webServer;

ServletContext servletContext = getServletContext();

if (webServer == null && servletContext == null) {

ServletWebServerFactory factory = getWebServerFactory();

this.webServer = factory.getWebServer(getSelfInitializer());

}

else if (servletContext != null) {

try {

getSelfInitializer().onStartup(servletContext);

}

catch (ServletException ex) {

throw new ApplicationContextException("Cannot initialize servlet context",

ex);

}

}

initPropertySources();

}

  • 用的工厂模式,先找到工厂getWebServerFactory()
  • 再用工厂生成webServer, factory.getWebServer(getSelfInitializer())
  • 先看看 getWebServerFactory() 这个方法

protected ServletWebServerFactory getWebServerFactory() {

// Use bean names so that we don't consider the hierarchy

String[] beanNames = getBeanFactory()

.getBeanNamesForType(ServletWebServerFactory.class);

if (beanNames.length == 0) {

throw new ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to missing "

+ "ServletWebServerFactory bean.");

}

if (beanNames.length > 1) {

throw new ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to multiple "

+ "ServletWebServerFactory beans : "

+ StringUtils.arrayToCommaDelimitedString(beanNames));

}

return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);

}

  • 获取唯一的工厂:ServletWebServerFactory,多了少了都不行
  • 在哪加载进springboot的呢?

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-autoconfigure</artifactId>

<scope>compile</scope>

</dependency>

  • spring-boot-starter-web里面是包含了spring-boot-starter依赖的
  • spring-boot-starter里面包含了spring-boot-autoconfigure依赖
  • spring-boot-autoconfigure里面有一个类:ServletWebServerFactoryConfiguration
  • 这个类里面有一个静态类: EmbeddedTomcat

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

static class EmbeddedTomcat {

@Bean

TomcatServletWebServerFactory tomcatServletWebServerFactory(

ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,

ObjectProvider<TomcatContextCustomizer> contextCustomizers,

ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {

TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

factory.getTomcatConnectorCustomizers()

.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));

factory.getTomcatContextCustomizers()

.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));

factory.getTomcatProtocolHandlerCustomizers()

.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));

return factory;

}

}

  • ConditionOnClass : 它依赖 Servlet.class, Tomcat.class, UpgradeProtocol.class
  • ConditionalOnMissingBean: 防止重复加载
  • 至此,factory怎么加载进spring就找到了
  • 我们多看一点,这个类ServletWebServerFactoryConfiguration还有两个静态类

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

static class EmbeddedJetty {

@Bean

JettyServletWebServerFactory JettyServletWebServerFactory(

ObjectProvider<JettyServerCustomizer> serverCustomizers) {

JettyServletWebServerFactory factory = new JettyServletWebServerFactory();

factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));

return factory;

}

}

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

static class EmbeddedUndertow {

@Bean

UndertowServletWebServerFactory undertowServletWebServerFactory(

ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,

ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {

UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();

factory.getDeploymentInfoCustomizers()

.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));

factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));

return factory;

}

}

  • 一个是生成jetty服务器工厂,一个是生成undertow服务器工厂
  • 它们的加载,取决于依赖的class是否存在
  • tomcat: Servlet.class, Tomcat.class, UpgradeProtocol.class
  • jetty: Servlet.class, Server.class, Loader.class, WebAppContext.class
  • undertow: Servlet.class, Undertow.class, SslClientAuthMode.class

假如我们想换成undertow服务器,依赖改了就行了

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-undertow</artifactId>

</dependency>

假如我们想换成jetty服务器,同理

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-jetty</artifactId>

</dependency>

  • 思维拉回来,工厂有了,我们看看工厂怎么生成的webServer

TomcatServletWebServerFactory.java

@Override

public WebServer getWebServer(ServletContextInitializer... initializers) {

if (this.disableMBeanRegistry) {

Registry.disableRegistry();

}

Tomcat tomcat = new Tomcat();

File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");

tomcat.setBaseDir(baseDir.getAbsolutePath());

Connector connector = new Connector(this.protocol);

connector.setThrowOnFailure(true);

tomcat.getService().addConnector(connector);

customizeConnector(connector);

tomcat.setConnector(connector);

tomcat.getHost().setAutoDeploy(false);

configureEngine(tomcat.getEngine());

for (Connector additionalConnector : this.additionalTomcatConnectors) {

tomcat.getService().addConnector(additionalConnector);

}

prepareContext(tomcat.getHost(), initializers);

return getTomcatWebServer(tomcat);

}

  • 这个方法我们就不分析了,就是生成tomcat服务器,和spring关联不大,改天我们专门分析tomcat源码
  • 至此,整个springboot的加载web服务器过程就完了

【Java】springboot源码解析-管中窥豹系列之web服务器(七)

欢迎关注微信公众号:丰极,更多技术学习分享。

javaspring源码springboot

阅读 42发布于 今天 09:44

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

avatar

丰极

1 声望

0 粉丝

0 条评论

得票时间

avatar

丰极

1 声望

0 粉丝

宣传栏

一、前言

  • Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去。
  • 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot源码管中窥豹系列。

【Java】springboot源码解析-管中窥豹系列之web服务器(七)

二、web服务器

  • 以前的的spring项目或者springmvc项目都需要一个web服务器,tomcat,或者其它的
  • 使用springboot之后,我们不再需要配置web服务器,因为springboot帮我们集成了
  • 今天我们来分析一下源码,看看在哪里实现的,知其然知其所以然

三、源码分析

  • 还是从SpringApplication的run方法开始看
  • 不熟悉的可以看之前的文章:springboot源码解析-管中窥豹系列之总体结构(一)

SpringApplication.java

public ConfigurableApplicationContext run(String... args) {

...

try {

...

refreshContext(context);

...

}

catch (Throwable ex) {

...

}

...

return context;

}

  • 接着进入到 refreshContext(context) 里面

AbstractApplicationContext.java

@Override

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

...

try {

...

// Initialize other special beans in specific context subclasses.

onRefresh();

...

}

catch (BeansException ex) {

...

}

finally {

...

}

}

}

  • 进入到 onRefresh() 方法

protected void onRefresh() throws BeansException {

// For subclasses: do nothing by default.

}

  • 注意这个是一个protected方法,我们进入到子实现里面
  • 具体用的哪个context,请看之前的文章:springboot源码解析-管中窥豹系列之项目类型(二)

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."

+ "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."

+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."

+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {

Class<?> contextClass = this.applicationContextClass;

if (contextClass == null) {

try {

switch (this.webApplicationType) {

case SERVLET:

contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);

break;

case REACTIVE:

contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);

break;

default:

contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);

}

}

catch (ClassNotFoundException ex) {

throw new IllegalStateException(

"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);

}

}

return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);

}

  • 最常用的就是普通web项目,我们看这一个
  • 我们到org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext里面找 onRefresh() 方法
  • 没找到,在父类ServletWebServerApplicationContext里面找到了

ServletWebServerApplicationContext.java

@Override

protected void onRefresh() {

super.onRefresh();

try {

createWebServer();

}

catch (Throwable ex) {

throw new ApplicationContextException("Unable to start web server", ex);

}

}

  • 我们到 createWebServer() 方法里面

private void createWebServer() {

WebServer webServer = this.webServer;

ServletContext servletContext = getServletContext();

if (webServer == null && servletContext == null) {

ServletWebServerFactory factory = getWebServerFactory();

this.webServer = factory.getWebServer(getSelfInitializer());

}

else if (servletContext != null) {

try {

getSelfInitializer().onStartup(servletContext);

}

catch (ServletException ex) {

throw new ApplicationContextException("Cannot initialize servlet context",

ex);

}

}

initPropertySources();

}

  • 用的工厂模式,先找到工厂getWebServerFactory()
  • 再用工厂生成webServer, factory.getWebServer(getSelfInitializer())
  • 先看看 getWebServerFactory() 这个方法

protected ServletWebServerFactory getWebServerFactory() {

// Use bean names so that we don't consider the hierarchy

String[] beanNames = getBeanFactory()

.getBeanNamesForType(ServletWebServerFactory.class);

if (beanNames.length == 0) {

throw new ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to missing "

+ "ServletWebServerFactory bean.");

}

if (beanNames.length > 1) {

throw new ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to multiple "

+ "ServletWebServerFactory beans : "

+ StringUtils.arrayToCommaDelimitedString(beanNames));

}

return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);

}

  • 获取唯一的工厂:ServletWebServerFactory,多了少了都不行
  • 在哪加载进springboot的呢?

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-autoconfigure</artifactId>

<scope>compile</scope>

</dependency>

  • spring-boot-starter-web里面是包含了spring-boot-starter依赖的
  • spring-boot-starter里面包含了spring-boot-autoconfigure依赖
  • spring-boot-autoconfigure里面有一个类:ServletWebServerFactoryConfiguration
  • 这个类里面有一个静态类: EmbeddedTomcat

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

static class EmbeddedTomcat {

@Bean

TomcatServletWebServerFactory tomcatServletWebServerFactory(

ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,

ObjectProvider<TomcatContextCustomizer> contextCustomizers,

ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {

TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

factory.getTomcatConnectorCustomizers()

.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));

factory.getTomcatContextCustomizers()

.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));

factory.getTomcatProtocolHandlerCustomizers()

.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));

return factory;

}

}

  • ConditionOnClass : 它依赖 Servlet.class, Tomcat.class, UpgradeProtocol.class
  • ConditionalOnMissingBean: 防止重复加载
  • 至此,factory怎么加载进spring就找到了
  • 我们多看一点,这个类ServletWebServerFactoryConfiguration还有两个静态类

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

static class EmbeddedJetty {

@Bean

JettyServletWebServerFactory JettyServletWebServerFactory(

ObjectProvider<JettyServerCustomizer> serverCustomizers) {

JettyServletWebServerFactory factory = new JettyServletWebServerFactory();

factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));

return factory;

}

}

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

static class EmbeddedUndertow {

@Bean

UndertowServletWebServerFactory undertowServletWebServerFactory(

ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,

ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {

UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();

factory.getDeploymentInfoCustomizers()

.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));

factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));

return factory;

}

}

  • 一个是生成jetty服务器工厂,一个是生成undertow服务器工厂
  • 它们的加载,取决于依赖的class是否存在
  • tomcat: Servlet.class, Tomcat.class, UpgradeProtocol.class
  • jetty: Servlet.class, Server.class, Loader.class, WebAppContext.class
  • undertow: Servlet.class, Undertow.class, SslClientAuthMode.class

假如我们想换成undertow服务器,依赖改了就行了

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-undertow</artifactId>

</dependency>

假如我们想换成jetty服务器,同理

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-jetty</artifactId>

</dependency>

  • 思维拉回来,工厂有了,我们看看工厂怎么生成的webServer

TomcatServletWebServerFactory.java

@Override

public WebServer getWebServer(ServletContextInitializer... initializers) {

if (this.disableMBeanRegistry) {

Registry.disableRegistry();

}

Tomcat tomcat = new Tomcat();

File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");

tomcat.setBaseDir(baseDir.getAbsolutePath());

Connector connector = new Connector(this.protocol);

connector.setThrowOnFailure(true);

tomcat.getService().addConnector(connector);

customizeConnector(connector);

tomcat.setConnector(connector);

tomcat.getHost().setAutoDeploy(false);

configureEngine(tomcat.getEngine());

for (Connector additionalConnector : this.additionalTomcatConnectors) {

tomcat.getService().addConnector(additionalConnector);

}

prepareContext(tomcat.getHost(), initializers);

return getTomcatWebServer(tomcat);

}

  • 这个方法我们就不分析了,就是生成tomcat服务器,和spring关联不大,改天我们专门分析tomcat源码
  • 至此,整个springboot的加载web服务器过程就完了

【Java】springboot源码解析-管中窥豹系列之web服务器(七)

欢迎关注微信公众号:丰极,更多技术学习分享。

以上是 【Java】springboot源码解析-管中窥豹系列之web服务器(七) 的全部内容, 来源链接: www.h5w3.com/112986.html

回到顶部