主要内容
1. ConfigurableEmbeddedServletContainer
2. WebServerFactoryCustomize
3. 注册Servlet,Filter,Listener
4. 使用其他Servlet容器
Jetty
Undertow
1. SpringBoot默认使用的是tomcat的servlet容器
1. SpringBoot默认使用的是tomcat的servlet容器
我们以前写一个web应用,我们会将这个web应用打成一个war包,然后在web配好tomcat环境。
tomcat它就是一个servlet容器。把我们的应用放到tomcat上,然后启动tomcat就行了。
我们注意到SpringBoot再跑web项目时,没有配置tomcat环境,直接启动。它默认用的就是自身
嵌入的tomcat。
2. 我们还会在本地装有tomcat软件
如果我们相对安装的tomcat进行一些定制优化设置时,可以来到conf目录下修改其中的个配置文件。
那么就会出现一个问题:SpringBoot中我们怎么定制优化tomcat呢?
2. 如何定制和修改Servlet容器的相关配置。
2.1 配置server.xxx属性
1. 在主配置文件中修改server.xxx 属性即可。
# 修改tomcat端口
server.port=8081
# 项目地访问路径
server.servlet.context-path=/crud
# 专门配置tomcat服务器的相关属性
# server.tomcat.uri-encoding=UTF-8
2. ServerProperties绑定和server有关的配置
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
}
即修改ServerProperties中的一些属性,都是修改服务器的一些配置。
3. 配置的两种模式
1. 通用的Servlet容器设置
server.xxx
2. 针对mcat有关的的设置
server.tomcat.xxx
2.2 编写一个嵌入式的Servlet容器定制器:WebServerFactoryCustomize
注意:在SpringBoot2.0以上用WebServerFactoryCustomizer接口来定制
EmbeddedServletContainerCustomizer被弃置
1. WebServerFactoryCustomizer接口
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
void customize(T factory);
}
2. 编写一个嵌入式的Servlet容器定制器:WebServerFactoryCustomizer
注意参数:<T extends WebServerFactory>
ConfigurableWebServerFactory extends WebServerFactory
注意要加到IOC中。
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
//返回一个匿名内部类形式的Servlet容器定制器
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
//定制嵌入式的Servlet容器的相关规则。
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8088);
}
};
}
小结:xxxCustomizer要敏感一些。
1.在SpringBoot中会有很多的xxxCustomizer帮组我们进行定制配置
2.类似很多的xxxConfigurer帮助我们进行扩展配置。
3. 注册Servlet三大组件
Servlet, Filter, Listener
1. ServletRegistrationBean
2. FilterRegistrationBean
3. ServletListenerRegistrationBean
3.1 背景
首先我们要明确,我们目前写的SpringBoot应用默认是以jar包的形式,再启动嵌入的tomcat来运行。
而不是创建一个标准的web应用的目录结构的程序。
如果是一个标准的web应用,目录下会有src/main/webapp目录,其中会有webapp/WEB-INF/web.xml。
我们可以将三大组件都在web.xml中进行注册。
即SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有
web.xml文件来进行组建的注册。
没有web.xml时,我们怎么注册这些组件呢?
当然可以注册,SpringBoot给我们提供了对应的这三种方式来注册三大组件:
1. ServletRegistrationBean
2. FilterRegistrationBean
3. ServletListenerRegistrationBean
3.2 注册Servlet
1. 自定义一个Servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello,MyServlet");
}
}
2. 将自定义的Servlet注册到SpringBoot中(放到IOC中)
@Configuration
public class MyServerConfig {
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean<MyServlet> servletRegistrationBean =
new ServletRegistrationBean<MyServlet>(new MyServlet(), "/myServlet");
return servletRegistrationBean;
}
}
将自定义的Servlet通过ServletRegistrationBean注册到SpringBoot中,自定义的Servlet就会起作用。
3. 效果展示
3.3 注册Filter
1. 自定义一个Filter
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter process...");
//放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
2. 将自定义的Filter注册到IOC中
/**
* 2.注册Filter
* 将自定义的Filter注册到IOC容器中
* @return
*/
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new MyFilter());
filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet"));
return filterRegistrationBean;
}
3. 效果演示
3.4 注册Listener
1. 自定义一个Listener
/**
* 监听ServletContext的Listener
* 监听web应用启动和销毁的Listener
*/
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized...web应用启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed...当前web项目销毁");
}
}
2. 将自定义的Listener注册到IOC中
/**
* 注册Listener
* @return
*/
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> listenerRegistrationBean =
new ServletListenerRegistrationBean<MyListener>(new MyListener());
return listenerRegistrationBean;
}
3. 效果演示
3.5 最好的示例:
SpringBoot帮我们自动配置SpringMvc时,自动的注册了SpringMvc的前端控制器:DispatcherServlet。
我们可以参考源码:
DispatcherServletAutoConfiguration
webMvcProperties
1. DispatcherServletAutoConfiguration
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
生成DispatcherServlet注册器:DispatcherServletRegistrationBean,从webMvcProperties
获取参数,指定了路径。
DispatcherServletRegistrationBean registration =
new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
2. webMvcProperties
点进来找到 private String path = "/";
所以也就是SpringBoot帮我们自动的注册了SpringMvc的前端控制器:DispatcherServlet默认拦截所有路径。
/ : 拦截所有请求,包括静态资源,但是不拦截JSP。
/* : 会拦截JSP。
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
public static class Servlet {
/**
* Path of the dispatcher servlet.
*/
private String path = "/";
}
}
分析:
path是WebMvcProperties中的静态代码类Servlet中的属性
所以我们可以通过spring.mvc.servlet.path来配置SpringMVC的DispatcherServlet的拦截路径。
# 配置DispatcherServlet的拦截路径
# spring.mvc.servlet.path=/
4. 如何使用其他的嵌入式Servlet容器。
以前默认使用的是tomcat。
SpringBoot也支持切换:
Jetty(适合长连接:持续的点对点连接)
Undertow(不支持JSP,并发性能很好)
1. ConfigurableWebServerFactory
在我们编写一个嵌入式的Servlet容器定制器时,用到一个接口:ConfigurableWebServerFactory
这个接口可以实现嵌入式Servlet容器的切换:
2. 排斥tomcat的启动器
3. 在pom.xml中引入其他Servlet容器。
<dependency>
<artifactId>spring-boot-starter-jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
4. 结果生效
编写的嵌入式的Servlet容器定制器定制的端口也生效了。
所以WebServerFactoryCustomizer是定制所有的Servlet容器
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
//定制嵌入式的Servlet容器的相关规则。
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8088);
}
};
}
5. 嵌入式Servlet容器自动配置原理
5.1 嵌入式Servlet容器自动启动原理
嵌入式Servlet容器是怎么自动配置上去的,怎么工作的。
1. 找到相关自动配置类:
ServletWebServerFactoryConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration
2. 没有ServletWebServerFactory服务器工厂且有Tomcat类时,添加Tomcat服务器工厂。
class ServletWebServerFactoryConfiguration {
@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;
}
}
分析一下:
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
判断当前IOC容器中是否有这几个类,如果我们导入了Tomcat依赖,那么就会满足这个条件
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
判断当前容器中是否没有这个组件:ServletWebServerFactory,ServletWeb服务器工厂。
满足这些条件后,就会创建TomcatServletWebServerFactory:
Tomcat服务器工厂。
3. 看一下ServletWeb服务器工厂:ServletWebServerFactory
@FunctionalInterface
public interface ServletWebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
分析一下:里面就只有一个方法。
getWebServer():获得一个web服务器。
4. 打开ServletWeb服务器工厂:ServletWebServerFactory的体系结构:
public interface ConfigurableServletWebServerFactory extends ServletWebServerFactory { }
其中有:
JettyServletWeb服务器工厂
UndertowServletWeb服务器工厂
TomcatServletWeb服务器工厂
这三个工厂就能创建对应的三个嵌入式Servlet容器。
5. AbstractServletWebServerFactory:抽象ServletWeb服务器工厂
打开AbstractServletWebServerFactory看一下。
里面由三个容器工厂。
6. 小结一下:ServletWebServerFactoryConfiguration
ServletWebServerFactoryConfiguration根据我们导入的依赖,来添加对应的Servlet容器工厂。
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
//添加tomcat容器工厂:作用就是创建嵌入式的Servlet容器
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedJetty {
//添加Jetty容器工厂
@Bean
JettyServletWebServerFactory JettyServletWebServerFactory(
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow {
//添加Undertow容器工厂。
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(
}
}
}
5.2 TomcatServletWebServerFactory:Tomcat服务器工厂
我们以tomcat容器工厂为例来看一下它做了什么。
我们发现他重写了:获取web服务器的方法
public WebServer getWebServer(ServletContextInitializer... initializers)()方法。
我们回顾一下这个方法的出处。
ServletWebServerFactory中的方法
1. TomcatServletWebServerFactory
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//用代码的方式创建了一个Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本环境
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//将配置好的Tomcat传入进去:调用的getTomcatWebServer方法,返回一个Tomcat服务器。
return this.getTomcatWebServer(tomcat);
}
//得到一个Tomcat服务器:调用了tomcat的构造器。
//this.getPort() >= 0 什么意思:这是一个布尔值。进到构造器看
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}
2. 来到了TomcatWebServer类 :tomcatweb服务器类
有tomcat服务器的构造方法
和初始化方法initialize():启动tomcat服务器
//tomcat的构造器:参数是Tomcat对象,端口大于>=0时自动启动,Shutdown对象
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
//构造器调用了initialize方法
this.initialize();
}
//initialize()方法
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
//这有个很关键的启动方法:tomcat在这里启动了。
this.tomcat.start();
this.rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
} catch (NamingException var5) {
}
this.startDaemonAwaitThread();
} catch (Exception var6) {
this.stopSilently();
this.destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", var6);
}
}
}
5.3 我们之前是怎么配置Servlet容器的
1. 在配置文件中配置:
ServerProperties绑定和server有关的配置
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
}
即修改ServerProperties中的一些属性,都是修改服务器的一些配置。
2 编写一个嵌入式的Servlet容器定制器:WebServerFactoryCustomize
注意:在SpringBoot2.0以上用WebServerFactoryCustomizer接口来定制
EmbeddedServletContainerCustomizer被弃置
1. WebServerFactoryCustomizer:web服务器工厂定制器接口
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
void customize(T factory);
}
2. 编写一个嵌入式的Servlet容器定制器:WebServerFactoryCustomizer
注意参数:<T extends WebServerFactory>
ConfigurableWebServerFactory extends WebServerFactory
注意要加到IOC中。
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
//定制嵌入式的Servlet容器的相关规则。
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8088);
}
};
}
5.4 分析一下Servlet容器定制器:WebServerFactoryCustomizer配置修改怎么生效
1. 之前一直分析的是:ServletWebServerFactoryConfiguration
我称呼它为:web服务器工厂配置类
它为SpringBoot添加了三个服务器工厂。
2. 我们现在看一下:ServletWebServerFactoryAutoConfiguration
我称呼它为:web服务器工厂自动配置类
3. 它其中导入一个BeanPostProcessorsRegistrar类
后置处理器的注册器:作用就是给IOC容器中导入一些组件。
@Configuration(proxyBeanMethods = false)
@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 {
}
4. 后置处理器注册器导入了哪些组件:
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private 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);
}
}
5. 两个重要的组件(一)
1. WebServerFactoryCustomizerBeanPostProcessor
Web服务器工厂定制器的后置处理器
作用:
bean初始化前后(刚创建完对象,还没赋值)执行一些初始化工作。
2. ErrorPageRegistrarBeanPostProcessor
6. 我们之前在配置Servlet容器时
我们自己编写一个嵌入式的Servlet容器定制器:由WebServerFactoryCustomize接口定制
这是web服务器工厂定制器接口:WebServerFactoryCustomize
Web服务器工厂定制器的后置处理器:WebServerFactoryCustomizerBeanPostProcessor
显然两者有明显的关联关系。
也就是这个定制器有一个后置处理器,我们看一下这个后置处理器的作用。
7. 来到Web服务器工厂定制器的后置处理器
WebServerFactoryCustomizerBeanPostProcessor
//在初始化之前:对象创建好还没属性赋值。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//如果当前组件是一个服务器工厂类的WebServerFactory组件
if (bean instanceof WebServerFactory) {
//就调用下面这个方法:初始化方法
this.postProcessBeforeInitialization((WebServerFactory)bean);
}
return bean;
}
//来到这个被调用的初始化方法:
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//第二个参数getCustomizers:得到所有的定制器
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
//再调用每一个定制器的:customize(webServerFactory);方法。给Servlet容器进行属性赋值
customizer.customize(webServerFactory);
});
}
//来到第二个参数调用的方法:getCustomizers():
private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
//重点在这里:getWebServerFactoryCustomizerBeans()得到了一个服务器工厂定制器的Bean
this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
//获取所有的服务器工厂定制器Bean所在的方法:getWebServerFactoryCustomizerBeans()
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
//从beanFactory(IOC)容器中按照类型获取服务器工厂定制器的Bean组件
return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
8. 到这里已经明了了:
1. WebServerFactoryCustomizer,Web服务器工厂定制器有一个后置处理器:
Web服务器工厂定制器的后置处理器:WebServerFactoryCustomizerBeanPostProcessor。
2. Web服务器工厂定制器的后置处理器,WebServerFactoryCustomizerBeanPostProcessor
来自服务器工厂自动配置类ServletWebServerFactoryAutoConfigurationweb导入的
后置处理器的注册器BeanPostProcessorsRegistrar。
自动配置类配置了后置处理器到IOC中。
它的作用在Web服务器工厂定制器初始化之前,创建好还没属性赋值,为期进行初始化。
3. 之前分析了:web服务器工厂配置类ServletWebServerFactoryConfiguration
它为SpringBoot添加了三个服务器工厂。
4. 我们之前给Servlet容器进行配置时
通过给容器中添加一个WebServerFactoryCustomizer组件,服务器工厂定制器的Bean组件。
这样服务器工厂定制器后置处理器再为服务器工厂定制器进行初始化时,就不仅仅只拿到默认的
服务器工厂定制器,也会拿到我们定制的服务器工厂定制器同其他的定制器一起来给tomcat服务器
工厂类TomcatServletWebServerFactory初始化赋值。
我们在自己写的服务器工厂定制器,定制Servlet容器的相关规则来达到我们给服务器进行配置的效果。
5.5 在主配置文件中为Servlet容器进行配置时的生效原理
# 修改tomcat端口
server.port=8081
# 专门配置tomcat服务器的相关属性
server.tomcat.uri-encoding=UTF-8
# 项目地访问路径
server.servlet.context-path=/crud
1. 我们这些Server属性是和ServerProperties绑定的
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
}
6 小结:这就是嵌入式Servlet容器自动配置原理
1. ServletWebServerFactoryConfiguration
ServletWeb服务器工厂配置类,根据我们的导包的情况来添加对应的容器工厂类
TomcatServletWebServerFactory:tomcat服务器工厂类
JettyServletWebServerFactory
2. 有一个web服务器工厂自动配置类:ServletWebServerFactoryAutoConfiguration
它引入了一个BeanPostProcessorsRegistrar类后置处理器的注册器:作用就是给IOC容器中导入一些组件。
3. 后置处理器的注册器向IOC注入了:WebServerFactoryCustomizerBeanPostProcessor
Web服务器工厂定制器的后置处理器
4. ServletWebServerFactoryConfiguration向容器中添加了某个容器工厂类组件时,比如
tomcat服务器工厂类,TomcatServletWebServerFactory
当这个tomcat服务器工厂类想要创建对象tomcat服务器对象时,
就会触发Web服务器工厂定制器的后置处理器,WebServerFactoryCustomizerBeanPostProcessor中的
一个方法:postProcessBeforeInitialization()
这个方法会拿到所有的WebServerFactoryCustomizer组件定制器,在实例化tomcat服务器时,来定制
Servlet容器(Tomcat)的相关配置。
5. Web服务器工厂定制器的后置处理器中有一个方法:postProcessBeforeInitialization()
在初始化之前:对象创建好还没属性赋值。
该方法会判断这个组件是不是WebServerFactory类型
if (bean instanceof WebServerFactory)
我们通过结构图,发现几个服务器工厂类都实现了该接口。
只要添加的是服务器工厂类Web服务器工厂定制器的后置处理器就会工作
6. Web服务器工厂定制器的后置处理器是怎么工作的呢?
他会从IOC中获取所有的WebServerFactoryCustomizer组件定制器(包括我们自己写的)
一起来给tomcat服务器工厂类TomcatServletWebServerFactory初始化赋值。
7. 为什么可以在WebServerFactoryCustomizer组件定制器来定制这些组件属性
web服务器工厂自动配置类:ServletWebServerFactoryAutoConfiguration会向IOC注入一个
服务器工厂定制器:ServletWebServerFactoryCustomizer
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
8. 这个服务器工厂定制器的一个有参构造:
private final ServerProperties serverProperties;
public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
所以这些构造器都和ServerProperties绑定了,所以可以在组件定制器来定制这些组件属性。
9. 这就是嵌入式Servlet容器自动配置原理
7 嵌入式的Servlet容器的启动原理。
7.1 TomcatServletWebServerFactory:Tomcat服务器工厂类
1. 我们以tomcat容器工厂为例来看一下它做了什么。
我们发现他重写了:获取web服务器的方法
public WebServer getWebServer(ServletContextInitializer... initializers)()方法。
我们回顾一下这个方法的出处。
ServletWebServerFactory中的方法
2. TomcatServletWebServerFactory
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//用代码的方式创建了一个Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本环境
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//将配置好的Tomcat传入进去:调用的getTomcatWebServer方法,返回一个Tomcat服务器。
return this.getTomcatWebServer(tomcat);
}
返回一个WebServer:
return this.getTomcatWebServer(tomcat);
3. 最后通过方法调用得到一个TomcatWebServer,并且启动Tomcat
1. TomcatServletWebServerFactory
//得到一个Tomcat服务器:调用了tomcat的构造器。
//this.getPort() >= 0 什么意思:这是一个布尔值。进到构造器看
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}
2. 来到了TomcatWebServer类 :tomcatweb服务器类
有tomcat服务器的构造方法
和初始化方法initialize():启动tomcat服务器
//tomcat的构造器:参数是Tomcat对象,端口大于>=0时自动启动,Shutdown对象
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
//构造器调用了initialize方法
this.initialize();
}
//initialize()方法
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
//这有个很关键的启动方法:tomcat在这里启动了。
this.tomcat.start();
this.rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
} catch (NamingException var5) {
}
this.startDaemonAwaitThread();
} catch (Exception var6) {
this.stopSilently();
this.destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", var6);
}
}
}
7.2 什么时候创建嵌入式的Servlet工厂?什么时候获取嵌入式的Servlet容器并启动
比如以Tomcat为例:
TomcatServletWebServerFactory:Tomcat服务器工厂类。
TomcatWebServer:Tomcat服务器。
1. 我在TomcatServletWebServerFactory的构造方法出打一个断点
在TomcatWebServer的构造方法处也打一个断点
2. Debug启动:
从下向上点,一点一点的看整个程序的启动过程。
7.3 创建嵌入式的Servlet工厂的步骤
1. SpringBoot启动运行主方法中的run方法
2. SpringBoot刷新IOC容器:
即创建IOC容器对象,并初始化容器,创建容器中的每一个组件。
3. 创建IOC时,会根据当前SpringBoot环境来创建。
如果是web环境,就创建web的IOC:contextClass
AnnotationConfigServletWebServerApplicationContext
Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
不满足条件时,也会创建一个默认的IOC。
AnnotationConfigReactiveWebServerApplicationContext
Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
4. IOC创建好后,又会刷新刚才创建好的IOC
5. 若干步都在执行这个过程刷新过程
当前的webIOC容器对象重写了父类的onRefresh(),他是抽象的IOC容器中的方法。
6. ServletWebServerApplicationContext:IOC容器类
这个重写的onRefresh()方法总,加入了createWebServer()方法
7. 也就是ServletWebServerApplicationContext:IOC容器类会创建web服务器。
createWebServer()
8. 往上点一下,或者点进方法内看一下;
第一步:getWebServerFactory()获得了一个ServletWeb服务器(容器)工厂。
ServletWebServerFactory对象
第二部: 点进获得了一个Web服务器(容器)工厂方法看一下。
发现就是从IOC容器中根据bean的名字获取这个ServletWebServerFactory
9. 这样我们就得到了嵌入式的Servlet工厂:ServletWebServerFactory
再下面就是getBean()获取组件和创建这个嵌入式的Servlet工厂 组件的流程了。
10. 最后根据我们导入的tomcat依赖,创建一个嵌入式的TomcatServlet工厂
TomcatServletWebServerFactory
7.4 获取嵌入式的Servlet容器并启动的步骤
1. 上面已经理清了嵌入式的TomcatServlet工厂的创建步骤,这个工厂组件就是在自动配置时,
给IOC容器加了一个嵌入式的Tomcat。
2. 获取到TomcatServletWebServerFactory组件时,就开始调用tomcatweb服务器类的构造方法,
创建tomcatweb服务器对象。
3. 当这个tomcat服务器工厂类TomcatServletWebServerFactory想要创建对象tomcat服务器对象时,
就会触发Web服务器工厂定制器的后置处理器,WebServerFactoryCustomizerBeanPostProcessor中的
一个方法:postProcessBeforeInitialization()
这个方法会拿到所有的WebServerFactoryCustomizer组件定制器,在实例化tomcat服务器时,来定制
Servlet容器(Tomcat)的相关配置。
4. 后置处理器处理器的左右,就是给组件在刚刚实例化还没有赋值属性时,对其进行初始化。
5. 使用tomcat服务器工厂类TomcatServletWebServerFactory获取Servlet容器Tomcat。
6. tomcat服务器工厂类TomcatServletWebServerFactory通过方法调用得到一个TomcatWebServer,
7. 来到了TomcatWebServer类 :tomcatweb服务器类
有tomcat服务器的构造方法
和初始化方法initialize():启动tomcat服务器
8. 注意:先启动嵌入式的Servlet容器,再将IOC容器中剩下的没有创建出来的对象获取出来。
7.5 嵌入式的Servlet容器的启动原理小结
一句话总结:
IOC容器启动的时候,会创建嵌入式的TomcatServlet工厂,再创建Servlet容器Tomcat,并启动。