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 是嵌入式 Servlet 容器的自动配置类, 该类在 spring-boot-autoconfigure.jar 中的 Web 模块可以找到.
我们可以看到 EmbeddedServletContainerAutoConfiguration 被配置在 spring.factories 中, 看过我前面文章的朋友应该知道 SpringBoot 自动配置的原理, 这里将 EmbeddedServletContainerAutoConfiguration 配置类加入到 IoC 容器中, 接着我们来具体看看这个配置类:
- @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
- @Configuration
- @ConditionalOnWebApplication// 在 Web 环境下才会起作用
- @Import(BeanPostProcessorsRegistrar.class)// 会 Import 一个内部类 BeanPostProcessorsRegistrar
- public class EmbeddedServletContainerAutoConfiguration {
- @Configuration
- // Tomcat 类和 Servlet 类必须在 classloader 中存在
- // 文章开头我们已经导入了 Web 的 starter, 其中包含 tomcat 和 SpringMvc
- // 那么 classPath 下会存在 Tomcat.class 和 Servlet.class
- @ConditionalOnClass({ Servlet.class, Tomcat.class })
- // 当前 Spring 容器中不存在 EmbeddedServletContainerFactory 类型的实例
- @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
- public static class EmbeddedTomcat {
- @Bean
- public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
- // 上述条件注解成立的话就会构造 TomcatEmbeddedServletContainerFactory 这个 EmbeddedServletContainerFactory
- return new TomcatEmbeddedServletContainerFactory();
- }
- }
- @Configuration
- @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
- WebAppContext.class })
- @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
- public static class EmbeddedJetty {
- @Bean
- public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
- return new JettyEmbeddedServletContainerFactory();
- }
- }
- @Configuration
- @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
- @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
- public static class EmbeddedUndertow {
- @Bean
- public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
- return new UndertowEmbeddedServletContainerFactory();
- }
- }
- //other code...
- }
在这个自动配置类中配置了三个容器工厂的 Bean, 分别是:
- TomcatEmbeddedServletContainerFactory
- JettyEmbeddedServletContainerFactory
- UndertowEmbeddedServletContainerFactory
这里以大家熟悉的 Tomcat 为例, 首先 Spring Boot 会判断当前环境中是否引入了 Servlet 和 Tomcat 依赖, 并且当前容器中没有自定义的 EmbeddedServletContainerFactory 的情况下, 则创建 Tomcat 容器工厂. 其他 Servlet 容器工厂也是同样的道理.
EmbeddedServletContainerFactory
嵌入式 Servlet 容器工厂
- public interface EmbeddedServletContainerFactory {
- EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers);
- }
内部只有一个方法, 用于获取嵌入式的 Servlet 容器.
该工厂接口主要有三个实现类, 分别对应三种嵌入式 Servlet 容器的工厂类, 如图所示:
TomcatEmbeddedServletContainerFactory
以 Tomcat 容器工厂 TomcatEmbeddedServletContainerFactory 类为例:
- public class TomcatEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
- //other code...
- @Override
- public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) {
- // 创建一个 Tomcat
- Tomcat tomcat = new Tomcat();
- // 配置 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);
- // 包装 tomcat 对象, 返回一个嵌入式 Tomcat 容器, 内部会启动该 tomcat 容器
- return getTomcatEmbeddedServletContainer(tomcat);
- }
- }
首先会创建一个 Tomcat 的对象, 并设置一些属性配置, 最后调用 getTomcatEmbeddedServletContainer(tomcat) 方法, 内部会启动 tomcat, 我们来看看:
- protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
- Tomcat tomcat) {
- return new TomcatEmbeddedServletContainer(tomcat, getPort()>= 0);
- }
该函数很简单, 就是来创建 Tomcat 容器并返回. 看看 TomcatEmbeddedServletContainer 类:
- public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
- public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
- Assert.notNull(tomcat, "Tomcat Server must not be null");
- this.tomcat = tomcat;
- this.autoStart = autoStart;
- // 初始化嵌入式 Tomcat 容器, 并启动 Tomcat
- initialize();
- }
- private void initialize() throws EmbeddedServletContainerException {
- TomcatEmbeddedServletContainer.logger
- .info("Tomcat initialized with port(s):" + getPortsDescription(false));
- synchronized (this.monitor) {
- try {
- addInstanceIdToEngineName();
- try {
- final Context context = findContext();
- context.addLifecycleListener(new LifecycleListener() {
- @Override
- public void lifecycleEvent(LifecycleEvent 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
- // 启动 tomcat
- this.tomcat.start();
- // We can re-throw failure exception directly in the main thread
- rethrowDeferredStartupExceptions();
- try {
- ContextBindings.bindClassLoader(context, getNamingToken(context),
- 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) {
- 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 = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 第一步: 获取并启动监听器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 第二步: 根据 SpringApplicationRunListeners 以及参数来准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); // 准备 Banner 打印器 - 就是启动 Spring Boot 的时候打印在 console 上的 ASCII 艺术字体 Banner printedBanner = printBanner(environment); // 第三步: 创建 Spring 容器 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 第四步: Spring 容器前置处理 prepareContext(context, environment, listeners, applicationArguments,printedBanner); // 第五步: 刷新容器 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); }
创建 IoC 容器, 如果是 Web 应用, 则创建 AnnotationConfigEmbeddedWebApplicationContext 的 IoC 容器; 如果不是, 则创建 AnnotationConfigApplicationContext 的 IoC 容器; 很明显我们创建的容器是 AnnotationConfigEmbeddedWebApplicationContext, 接着我们来看看第五步, 刷新容器 refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); // 调用容器的 refresh() 方法刷新容器 ((AbstractApplicationContext) applicationContext).refresh(); }
容器刷新过程
调用抽象父类 AbstractApplicationContext 的 refresh() 方法;
AbstractApplicationContext public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { /** * 刷新上下文环境 */ prepareRefresh(); /** * 初始化 BeanFactory, 解析 xml, 相当于之前的 XmlBeanFactory 的操作, */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /** * 为上下文准备 BeanFactory, 即对 BeanFactory 的各种功能进行填充, 如常用的注解 @Autowired @Qualifier 等 * 添加 ApplicationContextAwareProcessor 处理器 * 在依赖注入忽略实现 * Aware 的接口, 如 EnvironmentAware,ApplicationEventPublisherAware 等 * 注册依赖, 如一个 bean 的属性中含有 ApplicationEventPublisher(beanFactory), 则会将 beanFactory 的实例注入进去 */ prepareBeanFactory(beanFactory); try { /** * 提供子类覆盖的额外处理, 即子类处理自定义的 BeanFactoryPostProcess */ postProcessBeanFactory(beanFactory); /** * 激活各种 BeanFactory 处理器, 包括 BeanDefinitionRegistryBeanFactoryPostProcessor 和普通的 BeanFactoryPostProcessor * 执行对应的 postProcessBeanDefinitionRegistry 方法 和 postProcessBeanFactory 方法 */ invokeBeanFactoryPostProcessors(beanFactory); /** * 注册拦截 Bean 创建的 Bean 处理器, 即注册 BeanPostProcessor, 不是 BeanFactoryPostProcessor, 注意两者的区别 * 注意, 这里仅仅是注册, 并不会执行对应的方法, 将在 bean 的实例化时执行对应的方法 */ registerBeanPostProcessors(beanFactory); /** * 初始化上下文中的资源文件, 如国际化文件的处理等 */ initMessageSource(); /** * 初始化上下文事件广播器, 并放入 applicatioEventMulticaster, 如 ApplicationEventPublisher */ initApplicationEventMulticaster(); /** * 给子类扩展初始化其他 Bean */ onRefresh(); /** * 在所有 bean 中查找 listener bean, 然后注册到广播器中 */ registerListeners(); /** * 设置转换器 * 注册一个默认的属性值解析器 * 冻结所有的 bean 定义, 说明注册的 bean 定义将不能被修改或进一步的处理 * 初始化剩余的非惰性的 bean, 即初始化非延迟加载的 bean */ finishBeanFactoryInitialization(beanFactory); /** * 通过 spring 的事件发布机制发布 ContextRefreshedEvent 事件, 以保证对应的监听器做进一步的处理 * 即对那种在 spring 启动后需要处理的一些类, 这些类实现了 ApplicationListener<ContextRefreshedEvent>, * 这里就是要触发这些类的执行 (执行 onApplicationEvent 方法) * spring 的内置 Event 有 ContextClosedEvent,ContextRefreshedEvent,ContextStartedEvent,ContextStoppedEvent,RequestHandleEvent * 完成初始化, 通知生命周期处理器 lifeCycleProcessor 刷新过程, 同时发出 ContextRefreshEvent 通知其他人 */ finishRefresh(); } finally { resetCommonCaches(); } } }
我们看第 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 容器:
private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServletContext(); if (localContainer == null && localServletContext == null) { // 先获取嵌入式 Servlet 容器工厂 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); // 根据容器工厂来获取对应的嵌入式 Servlet 容器 this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer()); } else if (localServletContext != null) { try { getSelfInitializer().onStartup(localServletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context",ex); } } initPropertySources(); }
关键代码在第 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 实例的注册, 我们留在下一篇具体讲
来源: https://www.cnblogs.com/java-chen-hao/p/11837057.html