概述

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. ubuntu 16.04 jdk-8u201-linux-x64.tar.gz 安装部署

    都是在普通用户加sudo代替root 1.sudo tar -zxvf jdk-8u201-linux-x64.tar.gz2.sudo chown make:make jdk1.8.0/3.sudo ...

  2. XML文件定义约束

    今天在做Android项目的时候,用到了XML解析,服务端返回的不是JSON,而是XML,这时候就需要我们解析XML了,当然在解析XML的时候,需要了XML文件的定义结构,任何一个文件的定义都是要遵循 ...

  3. 经典单调栈最大子矩形——牛客多校第二场H

    题目是求次大子矩形,那么在求最大子矩形的时候维护M1,M2即可 转移M2时比较的过程要注意一下 #include<bits/stdc++.h> using namespace std; # ...

  4. 关于windows下远程连接Linux服务器的方法(CentOs)

    1.服务器端安装VNC 1) 安装vncserver yum install -y tigervnc-server 2) 修改配置 vi /etc/sysconfig/vncservers   最后两 ...

  5. delphi windows操作

    输入 procedure TypeKeyString(s: string); var c: Char; i: integer; off: integer; vkw: Word; begin to Le ...

  6. delphi directui 做界面的一个例子

    现在很多CS系统或者软件界面都做的非常好看,比如:QQ皮肤,迅雷下载,360杀毒界面等.这些都是运用的流行的DIRECTUI 技术,基本上有的资料都是基于C++的,很少有同仁将C++下的DIRECT ...

  7. IIS反向代理解决Web前端跨域

    1.1 IIS7反向代理解决跨域问题IIS的版本必须是IIS7及其以上,否则没有反向代理功能:按照以下步骤来配置IIS,以实现反向代理: 1.2 配置步骤1. 下载安装ARR(Application ...

  8. POJ3241 最小曼哈顿距离生成树 - 真有趣哇

    目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog Problem:Portal传送门  原题目描述在最下面.  给你n个坐标, ...

  9. ArcGIS中QueryTask,FindTask,IndentifyTask 之间的区别

    1:QueryTask是一个进行空间和属性查询的功能类,它可以在某个地图服务的某个子图层内进行查询,顺便需要提一下的是,QueryTask进行查询的地图服务并 不必项加载到Map中进行显示.Query ...

  10. varnish(转http://www.ttlsa.com/nginx/varnish-4-configure-file/)

    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ...