Java EE - Servlet 3.0 和 Spring MVC
Table of Contents
- 前言
- 基于 Java 的配置
- ServletContainerInitializer
- 动态配置
- DispatcherServlet 和 ContextLoaderListener
- 两个应用上下文
- 配置过程
- 结语
- 参考链接
前言
在学习 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[] { "/" };
}
}
这个配置的作用为:
- 配置 DispatcherServlet 的路径映射为
/, 也就是说所有的请求都会经由 DispatcherServlet 进行处理 - 指定 DispatcherServlet 中的应用上下文配置类为 WebConfig
- 指定 ContextLoaderListener 中的应用上下文配置类为 RootConfig
由于在看 《Head First Servlet & JSP》的时候配置都是基于 XML 的,因此在看到《Spring 实战》的这一部分的时候我产生了不少的疑惑,其中包括:
- DispatcherServlet 是什么?是我们自己编写的 Servlet 还是 Spring MVC 自带的?
- ContextLoaderListener 是什么?新种类的 Listener?
- 我没有在 DD 中配置 DispatcherServlet,Servlet 容器怎么知道把请求发送给 DispatcherServlet?
- Spring MVC 中存在两个应用上下文?
- 基于 XML 又该怎样配置?
- ……
这里面的一些问题可以通过看书解决,但有一些问题,还是需要查阅网上的资料才行。
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,不是新品种的监听者。
这两个类的源码链接如下:
- spring-framework/ContextLoaderListener.java at master · spring-projects/spring-framework
- spring-framework/DispatcherServlet.java at master · spring-projects/spring-framework
两个应用上下文
在最开始的配置中,我们分别制定了 DispatcherServlet 和 ContextLoaderListener 中的应用上下文的配置类,这意味着:
- Spring MVC 中存在两个应用上下文,这两个应用上下文分别位于 DispatcherServlet 和 ContextLoaderListener
翻书可以得知,这两个应用上下文的作用分别为:
- DispatcherServlet 应用上下文用于加载应用中包含 Web 组件的 Bean,包括控制器、视图解析器……
- ContextLoaderListener 应用上下文用于加载应用中的其他 Bean,通常是驱动应用后端的中间层和数据层组件。
这样一来,我们便可以大致总结一下配置 DispatcherServlet 和 ContextLoaderListener 中需要配置的内容了:
- DispatcherServlet:普通 Servlet 对象需要的配置项,如路径映射、初始化参数。以及内部的 Spring 应用上下文的配置
- ContextLoaderListener:常规的应用上下文配置,比如初始化参数。以及内部的 Spring 应用上下文的配置。
配置过程
现在可以大致总结一下 Spring MVC 的配置过程了:
- Servlet 容器启动的时候查找 ServletContainerInitializer 的实现类,找到 SpringServletContainerInitializer
- 根据 SpringServletContainerInitializer 的 HandlesTypes 查找所有 WebApplicationInitializer 的实现类
- 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 给去了,所以……
参考链接
- 通过 ServletContainerInitializer 注册 Servlet 对象 - Elim 的博客 - ITeye 博客
- JavaEE 6 Servlet 3.0 中的新特性 - Oracle
- Servlet 3.0 新特性详解
Footnotes
1 书上这里说的是:如果能发现的话,就会用实现类来配置 Servlet 容器。感觉这种说法挺不准确的,不知道是不是翻译的锅。
Java EE - Servlet 3.0 和 Spring MVC的更多相关文章
- 关于 tomcat nio connector, servlet 3.0 async, spring mvc async 的关系
tomcat 的 org.apache.coyote.http11.Http11NioProtocol Connector 是一个使用 Java NIO 实现的异步 accept 请求的 connec ...
- 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旨在解决 ...
- SpringMVC(八):使用Servlet原生API作为Spring MVC hanlder方法的参数
在SpringMVC开发中,是有场景需要在Handler方法中直接使用ServletAPI. 在Spring MVC Handler的方法中都支持哪些Servlet API作为参数呢? --Respo ...
- JAVA入门教程 - idea 新建maven spring MVC项目
用的是Idea2017版本.其他大同小异 1.新建项目 2.勾选Create from archetype 选中maven-archetype-webapp 3.输入项目名字. 4.下一步 5.点Fi ...
- java web开发入门六(spring mvc)基于intellig idea
spring mvc ssm=spring mvc+spring +mybatis spring mvc工作流程 1A)客户端发出http请求,只要请求形式符合web.xml文件中配置的*.actio ...
- 【Java面试】说说你对Spring MVC的理解
一个工作了7年的粉丝,他说在面试之前,Spring这块的内容准备得很充分. 而且各种面试题也刷了,结果在面试的时候,面试官问:"说说你对Spring MVC的理解". 这个问题一下 ...
- Java EE - Servlet 小结
Table of Contents 前言 Servlet 的生命周期 Servlet 的初始化 ServletContext & ServletConfig 请求的处理 HttpServlet ...
- Java 之 Servlet 3.0
Servlet 3.0 好处: 支持注解配置,不需要 web.xml 文件了. 步骤: (1)创建 Java EE 项目,注意:JavaEE 版本必须6.0以上才支持Servlet3.0,可以不创建 ...
- Java EE Servlet相关的两个包
Servlet in Java EE 在Java EE的规范API中(链接),Servlet相关联的最重要的两个Package为: 1.javax.servlet 包含了一系列接口和类,他们在一个Se ...
随机推荐
- javaweb-servlet生成简单的验证码
package com.serv; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedIma ...
- JavaScript是什么
JavaScript是一种解释型语言而不是编译型语言,它往往被认为是一种脚本语言,而不被看作是一种真正的编程语言.也就是说,脚本语言比较简单,它们是非程序员所使用的编程语言. 如果一个程序员对Java ...
- css3创建多边形clip属性,可用来绘制不规则图形了
.path1 { clip-path: polygon(5px 10px, 16px 3px, 16px 17px); } .path2 { clip-path: polygon(3px 5px, 1 ...
- js字符串与十六进制之间的转换
在寻找加密解密的时候看到一个方法,代码图片转换.原理为:字符可以转为16进制,与图片RGB的一个R/G/B相对应,即一个像素点可容纳3个字符(注:Canvas的RGBA,透明度A似乎不能使用,使用后, ...
- C++ 中函数后面跟const是什么意思
问题:c++:void display( ) const 中的const是什么意思?简答:意思是除了表明了mutable的成员变量以外该类的其他的成员变量在这个函数内一律不能修改. 详细:加const ...
- Leet-code144. Binary Tree Preorder Traversal
这是一道将二叉树先序遍历,题目不难. 首先采用深搜递归 /** * Definition for a binary tree node. * public class TreeNode { * int ...
- 本号讯 | 人工智能手表为帕金森患者带来书写希望;微软翻译发布可实时翻译幻灯片的Presentation Translator
7 月 12 日,微软成立微软研究院人工智能中心(Microsoft Research AI).这是一个隶属于微软研究体系内的科研和孵化中心,将聚焦于解决最复杂的人工智能挑战. 这支由科学家和工程师组 ...
- JavaScript_2_实现
1. HTML中的脚本必须位于<script>与</script>标签之间 JavaScript是所有现代浏览器以及HTML5中的默认脚本语言 2. 脚本可被放置在HTML页面 ...
- mvc的help和functions语法
@helper show(int num ) { ) { @:存在 } else { @:不存在 } } @functions { /// <summary> /// 方法必须要求为静态 ...
- JS实现单向链表、双向链表、循环链表
https://cloud.tencent.com/developer/article/1114246 链表存储有序的元素的集合,但是和数组不同的是,链表中的元素在内存中的存储并不是连续的.每一个链表 ...