Table of Contents

  1. 前言
  2. 基于 Java 的配置
  3. ServletContainerInitializer
  4. 动态配置
  5. DispatcherServlet 和 ContextLoaderListener
  6. 两个应用上下文
  7. 配置过程
  8. 结语
  9. 参考链接

前言

在学习 Spring MVC 的过程中发现,Spring MVC 使用了不少 Servlet 3.0 的新特性,但鉴于我学习 Servlet 使用的教程是 《Head First Servlet & JSP》,其中的 Servlet 版本只有 2.5,因此不得不去研究一下 Servlet 3.0 的新特性在继续 Spring MVC 的学习。

这篇博客的主要内容便是在学习了 Servlet 3.0 的一些新特性之后对 Spring MVC 的启动配置过程的理解。

注意:博客的主要内容不是关于 Servlet 3.0 的新特性和 Spring MVC 的使用的。

Servlet 3.0 规范 2009 年就出来了,但是现在的教程基本上还是从 web.xml 开始配置的……

基于 Java 的配置

首先我们来看一下一个基于 Java 的配置,其实就是《Spring 实战》第五章的那个配置:

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import spittr.web.WebConfig;

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
} @Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
} @Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

这个配置的作用为:

  1. 配置 DispatcherServlet 的路径映射为 /, 也就是说所有的请求都会经由 DispatcherServlet 进行处理
  2. 指定 DispatcherServlet 中的应用上下文配置类为 WebConfig
  3. 指定 ContextLoaderListener 中的应用上下文配置类为 RootConfig

由于在看 《Head First Servlet & JSP》的时候配置都是基于 XML 的,因此在看到《Spring 实战》的这一部分的时候我产生了不少的疑惑,其中包括:

  1. DispatcherServlet 是什么?是我们自己编写的 Servlet 还是 Spring MVC 自带的?
  2. ContextLoaderListener 是什么?新种类的 Listener?
  3. 我没有在 DD 中配置 DispatcherServlet,Servlet 容器怎么知道把请求发送给 DispatcherServlet?
  4. Spring MVC 中存在两个应用上下文?
  5. 基于 XML 又该怎样配置?
  6. ……

这里面的一些问题可以通过看书解决,但有一些问题,还是需要查阅网上的资料才行。

ServletContainerInitializer

Servlet 3.0 中的新特性还是不少的,其中一个新特性便是:

  • 在 Servlet 3.0 环境中,Servlet 容器会在类路径中查找实现了 ServletContainerInitializer 接口的类,如果能发现的话,就会在启动的时候自动调用实现类的 onStartup 方法1

Spring MVC 中的 ServletContainerInitializer 实现便是 SpringServletContainerInitializer,它的部分源码如下:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
// ...
}
}

onStartup 中的代码可以不用管,关键在于 SpringServletContainerInitializer 头上的那个注解:

@HandlesTypes(WebApplicationInitializer.class)

这个注解的意思是:初始化 SpringServletContainerInitializer 时扫描所有实现了 WebApplicationInitializer 接口的类,并作为参数 webAppInitializerClasses 传递给 onStartup 方法。

而我们在前面的配置中继承的 AbstractAnnotationConfigDispatcherServletInitializer 便是 WebApplicationInitializer 的一个便利的基础实现。

也就是说,Servlet 容器启动的时候会将我们的配置类发送给 SpringServletContainerInitializer 方法,然后在进行一系列的操作后完成 DispatcherServlet 的配置。

到了这里,大概能解决 Servlet 容器是怎么找到我们的配置类的,但还有一个问题是:Spring MVC 是怎么配置的 DispatcherServlet。

动态配置

Servlet 3.0 中的一个新特性便是支持动态配置,我们可以通过 ServletContext 获取 Registration 对象进行动态配置,比如:

ServletRegistration.Dynamic registratio = ServletContext.addServlet("appServlet", DispatcherServlet.class);
registratio.addMapping("/");

我们可以看到,SpringServletContainerInitializer 的 onStartup 是可以获得 ServletContext 的,因此,我们完全可以在 onStartup 内部完成 DispatcherServlet 的配置。

很好,Servlet 容器是怎么配置 DispatcherServlet 的问题大概可以解决了。

DispatcherServlet 和 ContextLoaderListener

通过查阅资料可以发现,DispatcherServlet 是 Spring MVC 框架自带的 Servlet,而 ContextLoaderListener 是 Spring MVC 自带的 ServletContextListener,不是新品种的监听者。

这两个类的源码链接如下:

两个应用上下文

在最开始的配置中,我们分别制定了 DispatcherServlet 和 ContextLoaderListener 中的应用上下文的配置类,这意味着:

  • Spring MVC 中存在两个应用上下文,这两个应用上下文分别位于 DispatcherServlet 和 ContextLoaderListener

翻书可以得知,这两个应用上下文的作用分别为:

  • DispatcherServlet 应用上下文用于加载应用中包含 Web 组件的 Bean,包括控制器、视图解析器……
  • ContextLoaderListener 应用上下文用于加载应用中的其他 Bean,通常是驱动应用后端的中间层和数据层组件。

这样一来,我们便可以大致总结一下配置 DispatcherServlet 和 ContextLoaderListener 中需要配置的内容了:

  • DispatcherServlet:普通 Servlet 对象需要的配置项,如路径映射、初始化参数。以及内部的 Spring 应用上下文的配置
  • ContextLoaderListener:常规的应用上下文配置,比如初始化参数。以及内部的 Spring 应用上下文的配置。

配置过程

现在可以大致总结一下 Spring MVC 的配置过程了:

  1. Servlet 容器启动的时候查找 ServletContainerInitializer 的实现类,找到 SpringServletContainerInitializer
  2. 根据 SpringServletContainerInitializer 的 HandlesTypes 查找所有 WebApplicationInitializer 的实现类
  3. SpringServletContainerInitializer 的 onStartup 在内部的调用流程中创建并配置 DispatcherServlet 和 ContextLoaderListener

当然了,没有看源码的话,还有一个细节是不清楚的:

  • 应用上下文的创建是在 DispatcherServlet 和 ContextLoaderListener 内部完成的还是在外部创建好后传递给 DispatcherServlet 和 ContextLoaderListener

当然了,这无伤大雅 ( ̄▽ ̄)

之前还一直在想 Servlet 容器是先调用 ServletContainerInitializer 还是先调用 ServletContextListener,然后突然翻译过来,我都没有配置 Listener,

那肯定只能是 ServletContainerInitializer 了啊 (°∀°)ノ

更新:

  • 2019.05.11 - 看了一下官方文档,两个应用上下文都是在创建好以后再分配给 DispatcherServlet 和 ContextLoaderListener 的,同时 DispatcherServlet 中的应用上下文是 ContextLoaderListener 的子应用上下文,当 DispatcherServlet 中找不到需要的 Bean 时,就会委托给 ContextLoaderListener 应用上下文查找。

结语

本来还说贴一下 XML 的配置代码,结果《Spring 实战》提供的样例代码中直接把 web.xml 给去了,所以……

参考链接

Footnotes

1 书上这里说的是:如果能发现的话,就会用实现类来配置 Servlet 容器。感觉这种说法挺不准确的,不知道是不是翻译的锅。

Java EE - Servlet 3.0 和 Spring MVC的更多相关文章

  1. 关于 tomcat nio connector, servlet 3.0 async, spring mvc async 的关系

    tomcat 的 org.apache.coyote.http11.Http11NioProtocol Connector 是一个使用 Java NIO 实现的异步 accept 请求的 connec ...

  2. Ed Burns谈HTTP/2和Java EE Servlet 4规范

    在2015年JavaLand大会上,Ed Burns展示了Java EE Servlet 4.0规范(JSR 369)的概要,演讲的重点在于Java EE平台对HTTP/2的支持.HTTP/2旨在解决 ...

  3. SpringMVC(八):使用Servlet原生API作为Spring MVC hanlder方法的参数

    在SpringMVC开发中,是有场景需要在Handler方法中直接使用ServletAPI. 在Spring MVC Handler的方法中都支持哪些Servlet API作为参数呢? --Respo ...

  4. JAVA入门教程 - idea 新建maven spring MVC项目

    用的是Idea2017版本.其他大同小异 1.新建项目 2.勾选Create from archetype 选中maven-archetype-webapp 3.输入项目名字. 4.下一步 5.点Fi ...

  5. java web开发入门六(spring mvc)基于intellig idea

    spring mvc ssm=spring mvc+spring +mybatis spring mvc工作流程 1A)客户端发出http请求,只要请求形式符合web.xml文件中配置的*.actio ...

  6. 【Java面试】说说你对Spring MVC的理解

    一个工作了7年的粉丝,他说在面试之前,Spring这块的内容准备得很充分. 而且各种面试题也刷了,结果在面试的时候,面试官问:"说说你对Spring MVC的理解". 这个问题一下 ...

  7. Java EE - Servlet 小结

    Table of Contents 前言 Servlet 的生命周期 Servlet 的初始化 ServletContext & ServletConfig 请求的处理 HttpServlet ...

  8. Java 之 Servlet 3.0

    Servlet 3.0 好处: 支持注解配置,不需要 web.xml 文件了. 步骤: (1)创建 Java EE 项目,注意:JavaEE 版本必须6.0以上才支持Servlet3.0,可以不创建 ...

  9. Java EE Servlet相关的两个包

    Servlet in Java EE 在Java EE的规范API中(链接),Servlet相关联的最重要的两个Package为: 1.javax.servlet 包含了一系列接口和类,他们在一个Se ...

随机推荐

  1. HTML5:使用postMessage实现Ajax跨域请求

    HTML5:使用postMessage实现Ajax跨域请求 由于同源策略的限制,Javascript存在跨域通信的问题,典型的跨域问题有iframe与父级的通信等. 常规的几种解决方法: (1) do ...

  2. Ionic 2 中的创建一个闪视卡片组件

    闪视卡片是记忆信息的重要工具,它的使用可以追溯到19世纪.我们将要创建一个很酷的短暂动画来实现它.看起来像是这个样子的: 闪视卡片示例 Ionic 2 实例开发 新增章节将为你介绍如何在Ionic 2 ...

  3. Elasticsearch-基本操作1

    Elasticsearch版本:6.0 一.文档 一个文档不仅包含数据,也包含元数据,三个必须的元数据如下 _index:具有共同特性分到一起的文档集合,标示了文档的存放位置: 名字小写,不以下划线开 ...

  4. (转载)资源字典(Pro WPF 学习)

    原地址:http://www.cnblogs.com/yxhq/archive/2012/07/09/2582508.html 1.创建资源字典 下面是一个资源字典(AppBrushes.xaml), ...

  5. mysql-练级查询

    mysql的链接查询中主要有五大类链接查询 1.内连接查询 1.1:等值链接查询:指使用等号"="比较两个表的连接列的值,相当于两表执行笛卡尔后,取两表连结列值相等的记录. SEL ...

  6. 如何选择Web开发框架

    下面先来看看为什么要使用Web开发框架一 使用框架的必然性框架,即framework.其实就是某种应用的半成品,把不同应用程序中有共性的一些东西抽取出来,做成一个半成品程序,这样的半成品就是所谓的程序 ...

  7. 关于火狐浏览器在ubuntu和安卓手机上的同步

    最近在ubuntu使用火狐浏览器,感觉还不错.我想着,如果在我的安卓手机上装一个火狐浏览器,我就可以在手机上查看电脑上所收藏的网站了.然后我就去安卓应用市场下载了最新版的火狐浏览器.令人奇怪的是,我在 ...

  8. IT界程序员几大恶习能立即让你变穷,你有吗?

    IT软件开发,确实是各行业中薪水排名靠前的职业,月薪在八千以上的Java程序员多不胜数,但是不知有没有以下几种恶习?如果粘上一种,哪怕你薪水几万,估计最后也是囊中羞涩:综上所述列举以下几点,亲们自己对 ...

  9. Ubuntu下编译C++ OpenCV程序并运行

            因为想试跑yolov3的缘故,所以装了ubuntu系统,直接通过U盘装的,并不像他们说的“折腾”,反而一切非常顺利,比装软件还简单.然后就是要用C++跑opencv的程序用于比赛,出于 ...

  10. Python 求两个文本文件以行为单位的交集 并集 差集

    Python 求两个文本文件以行为单位的交集 并集 差集,来代码: s1 = set(open('a.txt','r').readlines()) s2 = set(open('b.txt','r') ...