Java开发从工作到原理--SpringBoot如何启动内置Tomcat

在Java开发从工作到原理--BasicErrorController统一异常处理中我们了解到SpringBoot基于Tomcat项目的ErrorPage功能,给我们默认配置了一个ErrorPage用于进行统一的异常处理,其中ErrorPage从Spring上下文到Tomcat上下文的处理过程是由TomcatServletWebServerFactory完成的。

那么我们先来看一下TomcatServletWebServerFactory的来源。

在IDEA中找到ServletWebServerFactoryAutoConfiguration类,是在spring-boot-autoconfigure包中。

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.web.servlet;

import javax.servlet.DispatcherType;
import javax.servlet.ServletRequest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.ForwardedHeaderFilter;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for servlet web servers.
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @author Ivan Sopov
 * @author Brian Clozel
 * @author Stephane Nicoll
 * @since 2.0.0
 */
@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 {

	@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);
	}

	@Bean
	@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
	@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
	public FilterRegistrationBean forwardedHeaderFilter() {
		ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
		FilterRegistrationBean registration = new FilterRegistrationBean<>(filter);
		registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
		registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
		return registration;
	}

	/**
	 * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
	 * {@link ImportBeanDefinitionRegistrar} for early registration.
	 */
	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);
			}
		}

	}

}

这个类通过Import注解引入了ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,

ServletWebServerFactoryConfiguration.EmbeddedJetty.class,

ServletWebServerFactoryConfiguration.EmbeddedUndertow.class

这四个类,继续进入ServletWebServerFactoryConfiguration查看对应的代码

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.web.servlet;

import java.util.stream.Collectors;

import javax.servlet.Servlet;

import io.undertow.Undertow;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.UpgradeProtocol;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.xnio.SslClientAuthMode;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer;
import org.springframework.boot.web.embedded.undertow.UndertowDeploymentInfoCustomizer;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Configuration classes for servlet web servers
 * 

* Those should be {@code @Import} in a regular auto-configuration class to guarantee * their order of execution. * * @author Phillip Webb * @author Dave Syer * @author Ivan Sopov * @author Brian Clozel * @author Stephane Nicoll * @author Raheela Asalm * @author Sergey Serdyuk */ @Configuration(proxyBeanMethods = false) class ServletWebServerFactoryConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider connectorCustomizers, ObjectProvider contextCustomizers, ObjectProvider> 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; } } /** * Nested configuration if Jetty is being used. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyServletWebServerFactory JettyServletWebServerFactory( ObjectProvider serverCustomizers) { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } /** * Nested configuration if Undertow is being used. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowServletWebServerFactory undertowServletWebServerFactory( ObjectProvider deploymentInfoCustomizers, ObjectProvider builderCustomizers) { UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.getDeploymentInfoCustomizers() .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList())); factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } }

可以看到是条件化装载对应的实例,起决定性作用的是三个ConditionalOnClass注解,EmbeddedTomcat的这个条件都满足要求,而EmbeddedUndertow和EmbeddedJetty的条件不符,在IDEA中可以清楚的看到有对应的类缺失,从这里可以看到TomcatServletWebServerFactory被加载到Spring的beanFactory中了。所以当程序在刷新ApplicationContext时,从beanFactory中按照ServletWebServerFactory类型获取到的是TomcatServletWebServerFactory实例。

确定了来源那么接下来看看TomcatServletWebServerFactory能干啥。

从名字上来说以Factory结尾的类,结合我们知道的工厂模式的功能,可以猜测这个类是用来创建web server的,tomcat 对应jetty和undertow,servlet对应reactive。

不难发现有一个getWebServer方法:

Java开发从工作到原理--SpringBoot如何启动内置Tomcat_第1张图片Tomcat内部层级关系

主要功能:

1、指定tomcat的临时文件存储地址,

2、创建connector对象并指定协议类型,并设置throwOnFailure为true

3、关联connector到tomcat对象中service对象

4、配置connector监听端口,协议信息,传输数据编码信息,设置bindOnInit为false,配置ssl,数据压缩功能

5、关联connector到tomcat对象;

6、设置host对象autoDeploy为false;

7、配置engine对象;

8、添加额外配置的connector到tomcat对象;

9、项目相关TomcatEmbeddedContext配置,并将TomcatEmbeddedContext关联到host对象。

10、启动tomcat,并将tomcat包装为TomcatWebServer返回

 

而上述第9项TomcatEmbeddedContext配置可更细分为:

1、设置contextpath

2、配置ServletContextInitializer到TomcatStarter(实现ServletContainerInitializer接口),用于Servlet,Filter,Listener,Session的配置。

3、LifecycleListener配置

4、ErrorPage配置

5、MimeMapping配置

6、Session超时时间,Cookie httpOnly配置,session持久化管理器配置

 

上述第10项在创建TomcatWebServer对象时调用其initialize方法

Java开发从工作到原理--SpringBoot如何启动内置Tomcat_第2张图片红框中打印的日志与项目启动日志信息吻合

Java开发从工作到原理--SpringBoot如何启动内置Tomcat_第3张图片

表明Tomcat正在启动,后续StandardService 以及StandardEngine信息表明Tomcat组件正在启动。

后面的 Tomcat started日志为完成ApplicationContext刷新之后调用TomcatWebServer.start方法进行相关检查,及启动状态更新时打印的。

 

相关配置都使用ServerProperties中的属性进行配置,基本配置项有:

server.port 监听端口

server.address 服务器有多个ip地址时,绑定监听的IP地址

server.error.path 默认error page访问地址

server.error.includeException 配置http code和异常类型

server.connectionTimeout 连接超时时间

server.ssl ssl相关配置

server.compression 数据压缩相关配置

server.http2.enabled 是否开启http2

server.servlet.contextPath 项目访问路径

server.servlet.contextParameters 对应web.xml中的信息

server.servlet.session.timeout 会话超时时间

server.servlet.session.cookie cookie的相关配置

server.tomcat.basedir tomcat基础路径

server.tomcat.maxThreads 最大工作线程数

server.tomcat.minSpareThreads 最小工作线程数

server.tomcat.maxConnections最大连接数

server.tomcat.acceptCount 等待连接队列大小

server.tomcat.connectionTimeout 连接等待时间

server.tomcat.resource.allowCaching 资源是否允许缓存 告诉浏览器进行缓存

server.tomcat.resource.cacheTtl 资源缓存时间,告诉浏览器缓存多久有效

 

这些在工作中可能会用到,还有可能会用到的就是基于ServletContextInitializer对象的Servlet,Filter,Listener,Session的配置。

ServletContextInitializer接口的实现类有如下:

剔除抽象类平常会用到的有FilterRegistrationBean,ServletRegistrationBean,ServletListenerRegistrationBean 具体使用方法很简单就不再多赘述了,SessionConfiguringInitializer为内部类,正常工作无法使用

 

你可能感兴趣的:(Java开发从工作到原理)