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 ...
随机推荐
- Ubuntu获取root 权限,开机自动登入root
新机器获取root权限,只需要给root 增加密码: sudo passwd root 修改开机自动登入: #sudo gedit /etc/lightdm/lightdm.conf 修改参数: au ...
- JVM的内存划分
1.栈内存:栈内存主要是用来运行函数的,在函数中定义的所有变量,都会在这个内存开辟空间. 在栈内存中定义的变量,不初始化,是不能直接使用的. 注意:所有的函数都必须在栈内存中运行. 而jvm只会运行处 ...
- VC 对话框设置背景颜色和图片
改变背景颜色,有两种方法: 1.在app的初始化函数中调用:void SetDialogBkColor( COLORREF clrCtlBk = RGB(192, 192, 192), COLORRE ...
- opencv将rgb图像转换成灰度图
python写法: import cv2 img = cv2.imread(img_dir, cv2.IMREAD_GRAYSCALE) cv2.imwrite(dis_dir, img) imrea ...
- 第1节 flume:12、flume的load_balance实现机制
1.5.flume的负载均衡load balancer 负载均衡是用于解决一台机器(一个进程)无法解决所有请求而产生的一种算法.Load balancing Sink Processor 能够实现 l ...
- webpack4 + vue多页面项目精细构建思路
#构建思路 虽然当前前端项目多以单页面为主,但多页面也并非一无是处,在一些情况下也是有用武之地的,比如: 项目庞大,各个业务模块需要解耦 SEO更容易优化 没有复杂的状态管理问题 可以实现页面单独上线 ...
- inner join 和 left join 的区别
1.left join.right join.inner join的区别 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表 ...
- Dapper学习总结
看了<Dapper从入门到精通>后的总结 (1)Dapper 是直接扩展 IDBConnection,而且是单独一个文件,可以直接嵌入到项目中使用. (2)通过手写sql语句,调用exec ...
- 01_12_JSP简介
01_12_JSP简介 1. JSP简介 JSP---Java Server Pages 拥有servlet的特性与优点(本身就是一个servlet) 直接在HTML中内嵌JSP代码 JSP程序有JS ...
- java在线聊天项目1.3版设计好友列表框功能补充,因只要用户登录就发送一串新列表,导致不同客户端好友列表不同问题
解决完毕后效果图: 好友列表Vector添加的时候进行判断,如果有相同的则不添加 int flag=0; for (int i = 0; i < names.size(); i++) { if ...