SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
目录
正文
Spring Boot 默认使用 Tomcat 作为嵌入式的 Servlet 容器,只要引入了 spring-boot-start-web 依赖,则默认是用 Tomcat 作为 Servlet 容器:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Servlet 容器的使用
默认 servlet 容器
我们看看 spring-boot-starter-web 这个 starter 中有什么
核心就是引入了 tomcat 和 SpringMvc,我们先来看 tomcat
Spring Boot 默认支持 Tomcat,Jetty,和 Undertow 作为底层容器。如图:
而 Spring Boot 默认使用 Tomcat,一旦引入 spring-boot-starter-web 模块,就默认使用 Tomcat 容器。
切换 servlet 容器
那如果我么想切换其他 Servlet 容器呢,只需如下两步:
- 将 tomcat 依赖移除掉
- 引入其他 Servlet 容器依赖
引入 jetty:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <!--移除 spring-boot-starter-web 中的 tomcat--> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency><dependency>
<groupId>org.springframework.boot</groupId>
<!--引入 jetty-->
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Servlet 容器自动配置原理
EmbeddedServletContainerAutoConfiguration
我们可以看到EmbeddedServletContainerAutoConfiguration 被配置在 spring.factories 中,看过我前面文章的朋友应该知道 SpringBoot 自动配置的原理,这里将 EmbeddedServletContainerAutoConfiguration 配置类加入到 IOC 容器中,接着我们来具体看看这个配置类:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication// 在 Web 环境下才会起作用 @Import(BeanPostProcessorsRegistrar.class)// 会 Import 一个内部类 BeanPostProcessorsRegistrar public class EmbeddedServletContainerAutoConfiguration {@Configuration </span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Tomcat类和Servlet类必须在classloader中存在 </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 文章开头我们已经导入了web的starter,其中包含tomcat和SpringMvc </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 那么classPath下会存在Tomcat.class和Servlet.class</span> @ConditionalOnClass({ Servlet.<span style="color: rgba(0, 0, 255, 1)">class</span>, Tomcat.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> }) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当前Spring容器中不存在EmbeddedServletContainerFactory类型的实例</span> @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.<span style="color: rgba(0, 0, 255, 1)">class</span>, search =<span style="color: rgba(0, 0, 0, 1)"> SearchStrategy.CURRENT) </span></strong><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> EmbeddedTomcat { @Bean </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { </span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 上述条件注解成立的话就会构造TomcatEmbeddedServletContainerFactory这个EmbeddedServletContainerFactory</span> <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong> TomcatEmbeddedServletContainerFactory();</strong> } } @Configuration @ConditionalOnClass({ Servlet.</span><span style="color: rgba(0, 0, 255, 1)">class</span>, Server.<span style="color: rgba(0, 0, 255, 1)">class</span>, Loader.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">, WebAppContext.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> }) @ConditionalOnMissingBean(value </span>= EmbeddedServletContainerFactory.<span style="color: rgba(0, 0, 255, 1)">class</span>, search =<span style="color: rgba(0, 0, 0, 1)"> SearchStrategy.CURRENT) </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> EmbeddedJetty { @Bean </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JettyEmbeddedServletContainerFactory(); } } @Configuration @ConditionalOnClass({ Servlet.</span><span style="color: rgba(0, 0, 255, 1)">class</span>, Undertow.<span style="color: rgba(0, 0, 255, 1)">class</span>, SslClientAuthMode.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> }) @ConditionalOnMissingBean(value </span>= EmbeddedServletContainerFactory.<span style="color: rgba(0, 0, 255, 1)">class</span>, search =<span style="color: rgba(0, 0, 0, 1)"> SearchStrategy.CURRENT) </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> EmbeddedUndertow { @Bean </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UndertowEmbeddedServletContainerFactory(); } } </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">other code...</span>
}
在这个自动配置类中配置了三个容器工厂的 Bean,分别是:
-
TomcatEmbeddedServletContainerFactory
-
JettyEmbeddedServletContainerFactory
-
UndertowEmbeddedServletContainerFactory
EmbeddedServletContainerFactory
- 嵌入式 Servlet 容器工厂
public interface EmbeddedServletContainerFactory {EmbeddedServletContainer <strong>getEmbeddedServletContainer</strong>( ServletContextInitializer... initializers);
}
内部只有一个方法,用于获取嵌入式的 Servlet 容器。
该工厂接口主要有三个实现类,分别对应三种嵌入式 Servlet 容器的工厂类,如图所示:
TomcatEmbeddedServletContainerFactory
以 Tomcat 容器工厂 TomcatEmbeddedServletContainerFactory 类为例:
public class TomcatEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {@Override public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) { //创建一个 Tomcat Tomcat tomcat = new Tomcat();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">other code...</span>
</span></strong><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配置Tomcat的基本环节</span></strong> File baseDir = (<span style="color: rgba(0, 0, 255, 1)">this</span>.baseDirectory != <span style="color: rgba(0, 0, 255, 1)">null</span> ? <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.baseDirectory: createTempDir(</span>"tomcat"<span style="color: rgba(0, 0, 0, 1)">)); <strong> tomcat.setBaseDir(baseDir.getAbsolutePath());</strong> Connector connector </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> Connector(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.protocol); <strong> tomcat.getService().addConnector(connector);</strong> customizeConnector(connector); <strong> tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(</strong></span><strong><span style="color: rgba(0, 0, 255, 1)">false</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong>);</strong> configureEngine(tomcat.getEngine()); </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Connector additionalConnector : <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); </span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">包装tomcat对象,返回一个嵌入式Tomcat容器,内部会启动该tomcat容器</span> <span style="color: rgba(0, 0, 255, 1)">return</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong> getTomcatEmbeddedServletContainer(tomcat);</strong> }
}
首先会创建一个 Tomcat 的对象,并设置一些属性配置,最后调用getTomcatEmbeddedServletContainer(tomcat) 方法,内部会启动 tomcat,我们来看看:
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) { return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0); }
该函数很简单,就是来创建 Tomcat 容器并返回。看看 TomcatEmbeddedServletContainer 类:
public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {</span><span style="color: rgba(0, 0, 255, 1)">public</span> TomcatEmbeddedServletContainer(Tomcat tomcat, <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> autoStart) { Assert.notNull(tomcat, </span>"Tomcat Server must not be null"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">this</span>.tomcat =<span style="color: rgba(0, 0, 0, 1)"> tomcat; </span><span style="color: rgba(0, 0, 255, 1)">this</span>.autoStart =<span style="color: rgba(0, 0, 0, 1)"> autoStart; </span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">初始化嵌入式Tomcat容器,并启动Tomcat</span>
initialize();
}</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> initialize() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> EmbeddedServletContainerException { TomcatEmbeddedServletContainer.logger .info(</span>"Tomcat initialized with port(s): " + getPortsDescription(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)); </span><span style="color: rgba(0, 0, 255, 1)">synchronized</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.monitor) { </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { addInstanceIdToEngineName(); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">final</span> Context context =<span style="color: rgba(0, 0, 0, 1)"> findContext(); context.addLifecycleListener(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> LifecycleListener() { @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> lifecycleEvent(LifecycleEvent event) { </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (context.equals(event.getSource()) </span>&&<span style="color: rgba(0, 0, 0, 1)"> Lifecycle.START_EVENT.equals(event.getType())) { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Remove service connectors so that protocol </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> binding doesn't happen when the service is </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> started.</span>
removeServiceConnectors();
}
}}); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Start the server to trigger initialization listeners </span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动tomcat</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.tomcat.start(); </span></strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We can re-throw failure exception directly in the main thread</span>
rethrowDeferredStartupExceptions();
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { ContextBindings.bindClassLoader(context, getNamingToken(context), getClass().getClassLoader()); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (NamingException ex) { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Naming is not enabled. Continue</span>
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Unlike Jetty, all Tomcat threads are daemon threads. We create a </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> blocking non-daemon to stop immediate shutdown</span>
startDaemonAwaitThread();
}
catch (Exception ex) {
containerCounter.decrementAndGet();
throw ex;
}
}
catch (Exception ex) {
stopSilently();
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
}
}
}
}
到这里就启动了嵌入式的 Servlet 容器,其他容器类似。
Servlet 容器启动原理
SpringBoot 启动过程
我们回顾一下前面讲解的 SpringBoot 启动过程,也就是 run 方法:
public ConfigurableApplicationContext run(String... args) { // 计时工具 StopWatch stopWatch = new StopWatch(); stopWatch.start();ConfigurableApplicationContext context </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; Collection</span><SpringBootExceptionReporter> exceptionReporters = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<><span style="color: rgba(0, 0, 0, 1)">(); configureHeadlessProperty(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一步:获取并启动监听器</span> SpringApplicationRunListeners listeners =<span style="color: rgba(0, 0, 0, 1)"> getRunListeners(args); listeners.starting(); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { ApplicationArguments applicationArguments </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DefaultApplicationArguments(args); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第二步:根据SpringApplicationRunListeners以及参数来准备环境</span> ConfigurableEnvironment environment =<span style="color: rgba(0, 0, 0, 1)"> prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体</span> Banner printedBanner =<span style="color: rgba(0, 0, 0, 1)"> printBanner(environment); </span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第三步:创建Spring容器</span> context =</strong><span style="color: rgba(0, 0, 0, 1)"><strong> createApplicationContext();</strong> exceptionReporters </span>=<span style="color: rgba(0, 0, 0, 1)"> getSpringFactoriesInstances( SpringBootExceptionReporter.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">, </span><span style="color: rgba(0, 0, 255, 1)">new</span> Class[] { ConfigurableApplicationContext.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> }, context); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第四步:Spring容器前置处理</span>
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
</span><strong><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第五步:刷新容器</span>
refreshContext(context);
// 第六步:Spring 容器后置处理
afterRefresh(context, applicationArguments);// 第七步:发出结束执行的事件
listeners.started(context);
// 第八步:执行 Runners
this.callRunners(context, applicationArguments);
stopWatch.stop();
// 返回容器
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
我们回顾一下第三步:创建 Spring 容器
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext";public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//根据应用环境,创建不同的 IOC 容器
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
private void refreshContext(ConfigurableApplicationContext context) {refresh(context); }protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//调用容器的 refresh() 方法刷新容器
((AbstractApplicationContext) applicationContext).refresh();
}
容器刷新过程
调用抽象父类 AbstractApplicationContext 的refresh() 方法;
AbstractApplicationContext
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 /** 4 * 刷新上下文环境 5 */ 6 prepareRefresh(); 7 8 /** 9 * 初始化 BeanFactory,解析 XML,相当于之前的 XmlBeanFactory 的操作, 10 */ 11 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 12 13 /** 14 * 为上下文准备 BeanFactory,即对 BeanFactory 的各种功能进行填充,如常用的注解 @Autowired @Qualifier 等 15 * 添加 ApplicationContextAwareProcessor 处理器 16 * 在依赖注入忽略实现 *Aware 的接口,如 EnvironmentAware、ApplicationEventPublisherAware 等 17 * 注册依赖,如一个 bean 的属性中含有 ApplicationEventPublisher(beanFactory),则会将 beanFactory 的实例注入进去 18 */ 19 prepareBeanFactory(beanFactory); 20 21 try { 22 /** 23 * 提供子类覆盖的额外处理,即子类处理自定义的 BeanFactoryPostProcess 24 */ 25 postProcessBeanFactory(beanFactory); 26 27 /** 28 * 激活各种 BeanFactory 处理器, 包括 BeanDefinitionRegistryBeanFactoryPostProcessor 和普通的 BeanFactoryPostProcessor 29 * 执行对应的 postProcessBeanDefinitionRegistry 方法 和 postProcessBeanFactory 方法 30 */ 31 invokeBeanFactoryPostProcessors(beanFactory); 32 33 /** 34 * 注册拦截 Bean 创建的 Bean 处理器,即注册 BeanPostProcessor,不是 BeanFactoryPostProcessor,注意两者的区别 35 * 注意,这里仅仅是注册,并不会执行对应的方法,将在 bean 的实例化时执行对应的方法 36 */ 37 registerBeanPostProcessors(beanFactory); 38 39 /** 40 * 初始化上下文中的资源文件,如国际化文件的处理等 41 */ 42 initMessageSource(); 43 44 /** 45 * 初始化上下文事件广播器,并放入 applicatioEventMulticaster, 如 ApplicationEventPublisher 46 */ 47 initApplicationEventMulticaster(); 48 49 /** 50 * 给子类扩展初始化其他 Bean 51 */ 52 onRefresh(); 53 54 /** 55 * 在所有 bean 中查找 listener bean,然后注册到广播器中 56 */ 57 registerListeners(); 58 59 /** 60 * 设置转换器 61 * 注册一个默认的属性值解析器 62 * 冻结所有的 bean 定义,说明注册的 bean 定义将不能被修改或进一步的处理 63 * 初始化剩余的非惰性的 bean,即初始化非延迟加载的 bean 64 */ 65 finishBeanFactoryInitialization(beanFactory); 66 67 /** 68 * 通过 spring 的事件发布机制发布 ContextRefreshedEvent 事件,以保证对应的监听器做进一步的处理 69 * 即对那种在 spring 启动后需要处理的一些类,这些类实现了 ApplicationListener<ContextRefreshedEvent>, 70 * 这里就是要触发这些类的执行 (执行 onApplicationEvent 方法) 71 * spring 的内置 Event 有 ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent 72 * 完成初始化,通知生命周期处理器 lifeCycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知其他人 73 */ 74 finishRefresh(); 75 } 76 77 finally { 78 79 resetCommonCaches(); 80 } 81 } 82 }
我们看第 52 行的方法:
protected void onRefresh() throws BeansException {}
很明显抽象父类 AbstractApplicationContext 中的 onRefresh 是一个空方法,并且使用 protected 修饰,也就是其子类可以重写 onRefresh 方法,那我们看看其子类 AnnotationConfigEmbeddedWebApplicationContext 中的 onRefresh 方法是如何重写的,AnnotationConfigEmbeddedWebApplicationContext 又继承 EmbeddedWebApplicationContext,如下:
public class AnnotationConfigEmbeddedWebApplicationContext extends EmbeddedWebApplicationContext {
那我们看看其父类 EmbeddedWebApplicationContext 是如何重写 onRefresh 方法的:
EmbeddedWebApplicationContext
@Override protected void onRefresh() { super.onRefresh(); try { //核心方法:会获取嵌入式的 Servlet 容器工厂,并通过工厂来获取 Servlet 容器 createEmbeddedServletContainer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start embedded container", ex);} }
在 createEmbeddedServletContainer 方法中会获取嵌入式的 Servlet 容器工厂,并通过工厂来获取 Servlet 容器:
1 private void createEmbeddedServletContainer() { 2 EmbeddedServletContainer localContainer = this.embeddedServletContainer; 3 ServletContext localServletContext = getServletContext(); 4 if (localContainer == null && localServletContext == null) { 5 //先获取嵌入式 Servlet 容器工厂 6 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); 7 //根据容器工厂来获取对应的嵌入式 Servlet 容器 8 this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer()); 9 } 10 else if (localServletContext != null) { 11 try { 12 getSelfInitializer().onStartup(localServletContext); 13 } 14 catch (ServletException ex) { 15 throw new ApplicationContextException("Cannot initialize servlet context",ex); 16 } 17 } 18 initPropertySources(); 19 }
关键代码在第 6 和第 8 行,先获取 Servlet 容器工厂,然后根据容器工厂来获取对应的嵌入式 Servlet 容器
获取 Servlet 容器工厂
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() { //从 Spring 的 IOC 容器中获取 EmbeddedServletContainerFactory.class 类型的 Bean String[] beanNames = getBeanFactory().getBeanNamesForType(EmbeddedServletContainerFactory.class); //调用 getBean 实例化 EmbeddedServletContainerFactory.class return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class); }
我们看到先从 Spring 的 IOC 容器中获取 EmbeddedServletContainerFactory.class 类型的 Bean,然后调用 getBean 实例化 EmbeddedServletContainerFactory.class,大家还记得我们第一节 Servlet 容器自动配置类 EmbeddedServletContainerAutoConfiguration 中注入 Spring 容器的对象是什么吗?当我们引入 spring-boot-starter-web 这个启动器后,会注入TomcatEmbeddedServletContainerFactory这个对象到 Spring 容器中,所以这里获取到的Servlet 容器工厂是TomcatEmbeddedServletContainerFactory,然后调用
TomcatEmbeddedServletContainerFactory 的 getEmbeddedServletContainer 方法获取 Servlet 容器,并且启动 Tomcat,大家可以看看文章开头的 getEmbeddedServletContainer 方法。
大家看一下第 8 行代码获取 Servlet 容器方法的参数 getSelfInitializer(),这是个啥?我们点进去看看
private ServletContextInitializer getSelfInitializer() { //创建一个 ServletContextInitializer 对象,并重写 onStartup 方法,很明显是一个回调方法 return new ServletContextInitializer() { public void onStartup(ServletContext servletContext) throws ServletException { EmbeddedWebApplicationContext.this.selfInitialize(servletContext); } }; }
创建一个 ServletContextInitializer 对象,并重写 onStartup 方法,很明显是一个回调方法,这里给大家留一点疑问:
- ServletContextInitializer 对象创建过程是怎样的?
- onStartup 是何时调用的?
- onStartup 方法的作用是什么?
ServletContextInitializer
是 Servlet 容器初始化的时候,提供的初始化接口。这里涉及到 Servlet、Filter 实例的注册,我们留在下一篇具体讲