本文源码分析基于
- spring-boot 2.1.4
- tomcat-embed 9.0.17
废话不多说,直接进入正题
启动 springboot
SpringApplication.run(SpringTestApplication.class, args);
进入
SpringApplication.run(String... args)
中的refreshContext(context)
(第316行)方法
这里的
context
是AnnotationConfigServletWebServerApplicationContext
我们来看下refreshContext(context)
方法的源码,如下:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);//从这里进去
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
- 进入
SpringApplication#refreshContext
中的refresh(context);
方法,源码如下
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();//这一步很重要,只要之spring应用都会调用ApplicationContext的refresh方法
}
因为AnnotationConfigServletWebServerApplicationContext
的父类ServletWebServerApplicationContext
重写了AbstractApplicationContext
的refresh
方法,我们来看下:
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
AbstractApplicationContext
是非常重要的一个类,几乎所有的ApplicationContext
都继承了AbstractApplicationContext
这里还是调用父类AbstractApplicationContext
中的refresh
方法,我们来看下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();//tomcat就是在这一步被启动的
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
-
AbstractApplicationContext#refresh
中的onRefresh()
方法
这一步会调用ServletWebServerApplicationContext
中onRefresh()
,因为ServletWebServerApplicationContext
重写了AbstractApplicationContext
的onRefresh()
方法。
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();//创建web服务器,即tomcat
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
-
ServletWebServerApplicationContext
中的createWebServer()
方法
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {//第一次启动会进到这里
ServletWebServerFactory factory = getWebServerFactory();//获取Server工厂(TomcatServletWebServerFactory)
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();
}
这里可以详细讲下ServletWebServerFactory
(本文就是TomcatServletWebServerFactory
)是怎么被spring生成的
TomcatServletWebServerFactory
是一个tomcat工厂,从中可以获取tomcat服务器。
首先引入与tomcat自动配置类的三个自动配置类,如下
ServletWebServerFactoryConfiguration
ServletWebServerFactoryAutoConfiguration
EmbeddedWebServerFactoryCustomizerAutoConfiguration
看下源码:
ServletWebServerFactoryConfiguration
@Configuration
class ServletWebServerFactoryConfiguration {
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class,
search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
//定义TomcatServletWebServerFactory的bean definition
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
...
...
...
}
- 定义
TomcatServletWebServerFactory
的bean definition。
ServletWebServerFactoryAutoConfiguration
@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 {
@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);
}
...
...
...
}
EmbeddedWebServerFactoryCustomizerAutoConfiguration
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
/**
* Nested configuration if Tomcat is being used.
*/
@Configuration
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
...
...
...
}
-
ServletWebServerFactoryCustomizer
主要配置通用的 servlet 的信息,包含端口、上下文路径、应用名、Session配置、Servlet 携带的初始变量等等 -
TomcatServletWebServerFactoryCustomizer
配置tomcat的额外信息,redirectContextRoot(是否在请求根上下文时转发,true则转发路径为/demoWeb/)和useRelativeRedirects(是否使用相对路径)等路径跳转问题处理 -
TomcatWebServerFactoryCustomizer
配置tomcat的主要信息,包含 remoteIpValue、connector(最大/最小可接收线程、最大可接收头部大小等等)、uriEncoding、connectionTimeout、maxConnection等属性
Spring通过ServletWebServerFactoryCustomizer
,TomcatServletWebServerFactoryCustomizer
, TomcatWebServerFactoryCustomizer
三个配置类对tomcat完成配置。
那么这些配置什么时候注入到Tomcat服务器的呢?
不卖关子了,也很简单,就是通过一个bean后置处理器完成注入的,看下这个后置处理器,如下:
/**
* {@link BeanPostProcessor} that applies all {@link WebServerFactoryCustomizer} beans
* from the bean factory to {@link WebServerFactory} beans.
*
* @author Dave Syer
* @author Phillip Webb
* @author Stephane Nicoll
* @since 2.0.0
*/
public class WebServerFactoryCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List> customizers;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"WebServerCustomizerBeanPostProcessor can only be used "
+ "with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof WebServerFactory) {//判断是否为WebServerFactory,TomcatServletWebServerFactory实现自WebServerFactory
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe
.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));//调用各个配置类中各个自定义方法,也就是完成配置
}
private Collection> 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;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory
.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();//获取自定义tomcat自定义配置,也就是上面的`ServletWebServerFactoryCustomizer,`TomcatServletWebServerFactoryCustomizer`, `TomcatWebServerFactoryCustomizer`三个配置类(其实还有其他的,这里我们不深入分析)
}
}
该后置处理器实现了BeanFactoryAware
说明它可以拿到一个BeanFactory
,这里为什么不用自动装配呢?因为,Spring说明AOP和自动装配无法一起使用。
就讲到这里把。
这里还有个地方比较难理解,就是getSelfInitializer()
方法,源码如下:
/**
* Returns the {@link ServletContextInitializer} that will be used to complete the
* setup of this {@link WebApplicationContext}.
* @return the self initializer
* @see #prepareWebApplicationContext(ServletContext)
*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
servletContext);
// 这一步是获取所有的 servlet 组件
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
以上代码等同于以下代码:
/**
* Returns the {@link ServletContextInitializer} that will be used to complete the
* setup of this {@link WebApplicationContext}.
* @return the self initializer
* @see #prepareWebApplicationContext(ServletContext)
*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return (servletContext) -> {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
}
就是使用了匿名内部类,这个 ServletContextInitializer 也很重要,它的作用就是注册所有的 servlet 组件 (包括 Servlet,Filter,Listener)
- 通过
ServletWebServerFactory(TomcatServletWebServerFactory)#getWebServer ()
方法创建tomcat并启动
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
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
}
这里就是创建了tomcat。
-
TomcatServletWebServerFactory#getWebServer
的getTomcatWebServer()
方法
/**
* Factory method called to create the {@link TomcatWebServer}. Subclasses can
* override this method to return a different {@link TomcatWebServer} or apply
* additional processing to the Tomcat server.
* @param tomcat the Tomcat server.
* @return a new {@link TomcatWebServer} instance
*/
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
/**
* Create a new {@link TomcatWebServer} instance.
* @param tomcat the underlying Tomcat server
* @param autoStart if the server should be started
*/
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
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(); //启动tomcat
// 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();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
this.tomcat.start();
启动tomcat。
TomcatStarter 实现了 ServletContainerInitializer。当服务器启动时,会调用 TomcatStarter#onStartup 进行Servlet 组件的初始化