概述

Spring Boot对所支持的Servlet Web服务器实现做了建模抽象:

Servlet容器类型  WebServer模型接口 WebServer工厂实现类
Tomcat    TomcatWebServer     TomcatServletWebServerFactory
Jetty    JettyWebServer    JettyServletWebServerFactory
Undertow  UndertowWebServer  UndertowServletWebServerFactory

基于此模型概念,在一个Servlet Web应用中,Spring Boot会使用上表中所说的WebServer工厂组件生成相应的WebServer实例。而这里的WebServer工厂组件又是从哪里来的呢 ? 这就是自动配置类ServletWebServerFactoryAutoConfiguration的任务了。

自动配置类ServletWebServerFactoryAutoConfiguration首先通过注解声明自己的生效条件:类 ServletRequest 存在于 classpath 上时才生效,也就是要求javax.servlet-api包必须被引用;当前应用必须是Spring MVC应用才生效;在以上条件被满足时,ServletWebServerFactoryAutoConfiguration引入了如下三个配置类 :

EmbeddedTomcat

EmbeddedJetty

EmbeddedUndertow

这三个配置类是ServletWebServerFactoryConfiguration的嵌套配置类,它们会分别检测classpath上存在的类,从而判断当前应用使用的是Tomcat/Jetty/Undertow中的哪一个Servlet Web服务器,从而决定定义相应的工厂组件bean : TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory。

详解:

ServletWebServerFactoryAutoConfiguration

/**
* EnableAutoConfiguration Auto-configuration for servlet web servers.
*
*/
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 仅在类 ServletRequest 存在于 classpath 上时才生效
@ConditionalOnClass(ServletRequest.class)
// 仅在当前应用是 Servlet Web 应用时才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 确保前缀为 server 的配置参数加载到 bean ServerProperties
@EnableConfigurationProperties(ServerProperties.class)
// 1. 导入 ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar 以注册
// BeanPostProcessor : WebServerFactoryCustomizerBeanPostProcessor 和
// ErrorPageRegistrarBeanPostProcessor
// 2. 导入 EmbeddedTomcat/EmbeddedJetty/EmbeddedUndertow 这三个属于
// ServletWebServerFactoryConfiguration 的嵌套配置类,这三个配置类会分别检测
// classpath上存在的类,从而判断当前应用使用的是 Tomcat/Jetty/Undertow,
// 从而决定定义哪一个 Servlet Web服务器的工厂 bean :
// TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration { // 定义 bean ServletWebServerFactoryCustomizer,这里与properties中serverProperties配置联系,解释了为什么配置文件也可修改servlet容器配置
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
} // 针对当前Servlet容器是Tomcat时定义该 bean,用于定制化 TomcatServletWebServerFactory
@Bean
// 仅在类 org.apache.catalina.startup.Tomcat 存在于 classpath 上时才生效
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
} /**
* Registers a WebServerFactoryCustomizerBeanPostProcessor. Registered via
* ImportBeanDefinitionRegistrar for early registration.
* 这是一个 ImportBeanDefinitionRegistrar, 它会向容器注入两个 BeanPostProcessor :
* 1. WebServerFactoryCustomizerBeanPostProcessor
* 该 BeanPostProcessor 会搜集容器中所有的 WebServerFactoryCustomizer,对当前应用所采用的
* WebServerFactory 被初始化前进行定制
* 2. ErrorPageRegistrarBeanPostProcessor
* 该 BeanPostProcessor 会搜集容器中所有的 ErrorPageRegistrar,添加到当前应用所采用的
* ErrorPageRegistry 中,实际上,这里的 ErrorPageRegistry 会是 ConfigurableWebServerFactory,
* 具体实现上来讲,是一个 ConfigurableTomcatWebServerFactory,ConfigurableJettyWebServerFactory
* 或者 ConfigurableUndertowWebServerFactory,分别对应 Tomcat, Jetty, Undertow 这三种
* Servlet Web 容器的工厂类
*/
public static class BeanPostProcessorsRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; @Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
} @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry,
"webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
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);
}
} }

ServletWebServerFactoryConfiguration

ServletWebServerFactoryConfiguration是一个针对ServletWebServerFactory进行配置的配置类。它通过检测应用classpath存在的类,从而判断当前应用要使用哪个Servlet容器:Tomcat,Jetty还是Undertow。检测出来之后,定义相应的Servlet Web服务器工厂组件bean

Servlet容器类型  WebServerFactory实现类
Tomcat  TomcatServletWebServerFactory
Jetty     JettyServletWebServerFactory
Undertow     UndertowServletWebServerFactory

注意,以上三个实现类都继承自抽象基类AbstractServletWebServerFactory,实现了接口WebServerFactory,ErrorPageRegistry。该特征会被WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor使用,用于对WebServerFactory进行定制化,以及设置相应的错误页面。

@Configuration
class ServletWebServerFactoryConfiguration { @Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat { @Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
} } /**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty { @Bean
public JettyServletWebServerFactory JettyServletWebServerFactory() {
return new JettyServletWebServerFactory();
} } /**
* Nested configuration if Undertow is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow { @Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
return new UndertowServletWebServerFactory();
} } }

查看代码:

这里以TomcatServletWebServerFactory:

注意这里此方法返回值:getTomcatWebServer(tomcat);查看此方法:传入两个参数,只要得到端口号大于0就默认启动

同时查看:其中TomcatWebServer类构造方法:

这时候tomcat启动开始。查看initialize();方法

private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName(); Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
}); // Start the server to trigger initialization listeners
this.tomcat.start(); // We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions(); try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
} // Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}

可以看到有启动: this.tomcat.start();启动!

思考

如何对嵌入式容器的配置修改是怎么生效的

两种方法:1、修改properties

2、配置servlet容器定制器

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> myWebServerFactoryCustomizer() {
return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
public void customize(TomcatServletWebServerFactory factory) {
//设置相关配置
factory.setPort(8080);
}
};
}

思考:怎么修改的原理?

springboot版本对照:

1.5.x:-->

//导入后置处理器的注册器,作用给容器导入组件

@Import(BeanPostProcessorsRegistrar.class

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
//导入后置处理器的注册器
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {.....}

2.x:将其作为内部类导入组件

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
.....
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
... @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
//导入servlet定制器的后置处理器
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
} }

观察方法:BeanPostProcessorsRegistrar,导入servlet定制器的后置处理器

作用:后置处理器:bean初始化前后(创建完对象,还没赋值赋值)执行初始化工作

WebServerFactoryCustomizerBeanPostProcessor

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}

初始化之前:判断如果当前初始化组件是WebServerFactory 的一个组件,就调用postProcessBeforeInitialization此方法,

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}

此方法:获取所有的定制器,调用每一个定制器的customize方法,来给servlet容器进行属性赋值

getCustomizers()方法以上调用:


private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
} this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());此方法点进去:
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}

作用:从容器中获取所有该类型的组件:WebServerFactoryCustomizer这里该组件我们可以定制。

思考2?在properties中是怎样定制servlet容器的??

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

2.x:中直接在servelt容器工厂的自动配置类:@EnableConfigurationProperties(ServerProperties.class)注解,向容器中添加ServerProperties组件,在SpringBoot中xxxProperties类都是和配置文件映射的类,用户可以通过配置文件配置类中属性。例如:用户可以通过server.port配置容器端口号。

1.5.x:serverProperties中实现方法。

嵌入式servlet容器自动配置原理的步骤

1)、SpringBoot根据导入的依赖情况,容器中导入的ServletWebServerFactoryConfiguration配置类,给容器添加相应的TomcatServletWebServerFactory,通过TomcatServletWebServerFactory的getWebServer()启动servelt容器

2)、容器中某个组件要创建对象就会惊动后置处理器;@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

只要是嵌入式的Servlet容器工厂,后置处理器就工作,用与定制嵌入式容器的配置修改,

3)、后置处理器,执行postProcessBeforeInitialization()方法,调用定制器的定制方法;

4_7.springboot2.x嵌入式servlet容器自动配置原理的更多相关文章

  1. springboot(八) 嵌入式Servlet容器自动配置原理和容器启动原理

    1.嵌入式Servlet容器自动配置原理 1.1 在spring-boot-autoconfigure-1.5.9.RELEASE.jar => springboot自动配置依赖 jar包下,E ...

  2. 【串线篇】spring boot嵌入式Servlet容器自动配置原理

    EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? @AutoConfigureOrder(Ordered.HIGHEST_PREC ...

  3. 嵌入式Servlet容器自动配置和启动原理

    EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? @AutoConfigureOrder(Ordered.HIGHEST_PRE ...

  4. 尚硅谷springboot学习26-嵌入式servlet容器自动配置、启动原理

    EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置 @AutoConfigureOrder(Ordered.HIGHEST_PREC ...

  5. 4_8.springboot2.x嵌入式servlet容器启动原理解析

    问题描述: 什么时候创建嵌入式的Servlet容器工厂? 什么时候获取嵌入式的Servlet容器并启动Tomcat? *获取嵌入式的Servlet容器工厂: 1).SpringBoot应用启动运行ru ...

  6. SpringBoot嵌入式Tomcat的自动配置原理

    在读本篇文章之前如果你读过这篇文章SpringBoot自动装配原理解析应该会更加轻松 准备工作 我们知道SpringBoot的自动装配的秘密在org.springframework.boot.auto ...

  7. 17. Spring Boot 配置嵌入式Servlet容器

    一.如何定制和修改Servlet容器的相关配置 1.配置文件(ServerProperties): 优先级最高 server.port=8081 server.context‐path=/crud s ...

  8. 配置嵌入式Servlet容器

    SpringBoot默认是用的是Tomcat作为嵌入式的Servlet容器:问题?1).如何定制和修改Servlet容器的相关配置:1.修改和server有关的配置(ServerProperties) ...

  9. SpringBoot配置嵌入式Servlet容器

    1).如何定制和修改Servlet容器的相关配置: 1.修改和server有关的配置(ServerProperties[也是EmbeddedServletContainerCustomizer]): ...

随机推荐

  1. wpf 解决 WPF SelectionChanged事件向上传递造成重复执行不想执行的函数的问题

    例如 tabcontrol里有一个tabitem tabitem里有一个combox和一个datagrid tabcontrol combox datagrid都有SelectionChanged事件 ...

  2. GlobalExceptionHandler 捕获抛出的异常,返回特定值给前端

    /** * @author hhh * @date 2019/1/17 16:28 * @Despriction */ @ResponseBody @ControllerAdvice @Log4j2 ...

  3. Go 动态类型声明

    Go 动态类型声明 package main import "fmt" func main() { var x float64 = 20.0 y := 42 fmt.Println ...

  4. bzoj1034题解

    [解题思路] 广义田忌赛马的贪心模型.如果当前实力最差的马比对手实力最差的马强,则匹配:如果当前实力最强的马比对手实力最强的马强,亦匹配:若上述两点均不成立,拿己方最差的马去匹配对手最强的马.复杂度O ...

  5. NX-二次开发删除对象UF_OBJ_delete_object

    NX9+VS2012 #include <uf.h> #include <uf_curve.h> #include <uf_obj.h> UF_initialize ...

  6. detours学习

    最近学习detours3.0,总结下学习过程,给后来学习者一点参考,也便于自己以后复习 首先应该知道detours可以干什么,学习之前最好看一下detours文档,这个文档很简单,只有4篇文章,相对比 ...

  7. Windows内核驱动开发入门学习资料

    声明:本文所描述的所有资料和源码均搜集自互联网,版权归原始作者所有,所以在引用资料时我尽量注明原始作者和出处:本文所搜集资料也仅供同学们学习之用,由于用作其他用途引起的责任纠纷,本人不负任何责任.(本 ...

  8. 制作windows puppet openstack镜像

    由于电信需要远程部署软件,拟使用puppet进行远程服务管理 前提 windows系统已经安装 windows已经安装virtio driver 远程桌面开启,最好关闭防火墙 设置MTU 在没有安装c ...

  9. 【POJ】2387 Til the Cows Come Home

    题目链接:http://poj.org/problem?id=2387 题意:求从1到n的最短路 题解:板子题.spfa. 代码: #include<iostream> #include& ...

  10. asp.net Core 使用redis(StackExchange.Redis)

    原文:asp.net Core 使用redis(StackExchange.Redis) 一.添加配置(appsettings.json) "Redis": { "Def ...