- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Spring MVC 启动过程源码分析详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
今天小编尝试从源码层面上对spring mvc的初始化过程进行分析,一起揭开spring mvc的真实面纱,也许我们都已经学会使用spring mvc,或者说对spring mvc的原理在理论上已经能倒背如流。在开始之前,这可能需要你掌握java ee的一些基本知识,比如说我们要先学会java ee 的servlet技术规范,因为spring mvc框架实现,底层是遵循servlet规范的.
在开始源码分析之前,我们可能需要一个简单的案例工程,不慌,小编已经安排好了:
样例工程下载地址 : https://github.com/smallercoder/spring-mvc-test 。
那下面就让我们开始吧! 。
1、前置知识 。
大家都知道,我们在使用spring mvc时通常会在 web.xml 文件中做如下配置:
web.xml 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<web-app version=
"3.0"
xmlns=
"http://java.sun.com/xml/ns/javaee"
xmlns:xsi=
"http://www.w3.org/2001/xmlschema-instance"
xsi:schemalocation=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
>
<!-- 上下文参数,在监听器中被使用 -->
<context-param>
<param-name>contextconfiglocation</param-name>
<param-value>
classpath:applicationcontext.xml
</param-value>
</context-param>
<!-- 监听器配置 -->
<listener>
<listener-
class
>org.springframework.web.context.contextloaderlistener</listener-
class
>
</listener>
<!-- 前端控制器配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-
class
>org.springframework.web.servlet.dispatcherservlet</servlet-
class
>
<init-param>
<param-name>contextconfiglocation</param-name>
<param-value>classpath:applicationcontext-mvc.xml</param-value>
</init-param>
<load-on-startup>
1
</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
|
上面的配置总结起来有几点内容,分别是:dispatcherservlet 。
当我们将spring mvc应用部署到tomcat时,当你不配置任何的 context-param 和 listener 参数,只配置一个 dispatcherservlet 时,那么tomcat在启动的时候是不会初始化spring web上下文的,换句话说,tomcat是不会初始化spring框架的,因为你并没有告诉它们spring的配置文件放在什么地方,以及怎么去加载。所以 listener 监听器帮了我们这个忙,那么为什么配置监听器之后就可以告诉tomcat怎么去加载呢?因为 listener 是实现了servlet技术规范的监听器组件,tomcat在启动时会先加载 web.xml 中是否有servlet监听器存在,有则启动它们。 contextloaderlistener 是spring框架对servlet监听器的一个封装,本质上还是一个servlet监听器,所以会被执行,但由于 contextloaderlistener 源码中是基于 contextconfiglocation 和 contextclass 两个配置参数去加载相应配置的,因此就有了我们配置的 context-param 参数了, servlet 标签里的初始化参数也是同样的道理,即告诉web服务器在启动的同时把spring web上下文( webapplicationcontext )也给初始化了.
上面讲了下tomcat加载spring mvc应用的大致流程,接下来将从源码入手分析启动原理.
2、spring mvc web 上下文启动源码分析 。
假设现在我们把上面 web.xml 文件中的 <load-on-startup>1</load-on-startup> 给去掉,那么默认tomcat启动时只会初始化spring web上下文,也就是说只会加载到 applicationcontext.xml 这个文件,对于 applicationcontext-mvc.xml 这个配置文件是加载不到的, <load-on-startup>1</load-on-startup> 的意思就是让 dispatcherservlet 延迟到使用的时候( 也就是处理请求的时候 )再做初始化.
我们已经知道spring web是基于 servlet 标准去封装的,那么很明显,servlet怎么初始化, webapplicationcontext web上下文就应该怎么初始化。我们先看看 contextloaderlistener 的源码是怎样的.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
contextloaderlistener
extends
contextloader
implements
servletcontextlistener {
// 初始化方法
@override
public
void
contextinitialized(servletcontextevent event) {
initwebapplicationcontext(event.getservletcontext());
}
// 销毁方法
@override
public
void
contextdestroyed(servletcontextevent event) {
closewebapplicationcontext(event.getservletcontext());
contextcleanuplistener.cleanupattributes(event.getservletcontext());
}
}
|
contextloaderlistener 类实现了 servletcontextlistener ,本质上是一个servlet监听器,tomcat将会优先加载servlet监听器组件,并调用 contextinitialized 方法,在 contextinitialized 方法中调用 initwebapplicationcontext 方法初始化spring web上下文,看到这焕然大悟,原来spring mvc的入口就在这里,哈哈~~~赶紧跟进去 initwebapplicationcontext 方法看看吧! 。
initwebapplicationcontext() 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 创建web上下文,默认是xmlwebapplicationcontext
if
(
this
.context ==
null
) {
this
.context = createwebapplicationcontext(servletcontext);
}
if
(
this
.context
instanceof
configurablewebapplicationcontext) {
configurablewebapplicationcontext cwac = (configurablewebapplicationcontext)
this
.context;
// 如果该容器还没有刷新过
if
(!cwac.isactive()) {
if
(cwac.getparent() ==
null
) {
applicationcontext parent = loadparentcontext(servletcontext);
cwac.setparent(parent);
}
// 配置并刷新容器
configureandrefreshwebapplicationcontext(cwac, servletcontext);
}
}
|
上面的方法只做了两件事:
1、如果spring web容器还没有创建,那么就创建一个全新的spring web容器,并且该容器为root根容器,下面第三节讲到的servlet spring web容器是在此根容器上创建起来的 。
2、配置并刷新容器 。
上面代码注释说到默认创建的上下文容器是 xmlwebapplicationcontext ,为什么不是其他web上下文呢?为啥不是下面上下文的任何一种呢?
我们可以跟进去 createwebapplicationcontext 后就可以发现默认是从一个叫 contextloader.properties 文件加载配置的,该文件的内容为:
。
。
具体实现为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
protected
class
<?> determinecontextclass(servletcontext servletcontext) {
// 自定义上下文,否则就默认创建xmlwebapplicationcontext
string contextclassname = servletcontext.getinitparameter(context_class_param);
if
(contextclassname !=
null
) {
try
{
return
classutils.forname(contextclassname, classutils.getdefaultclassloader());
}
catch
(classnotfoundexception ex) {
throw
new
applicationcontextexception(
"failed to load custom context class ["
+ contextclassname +
"]"
, ex);
}
}
else
{
// 从属性文件中加载类名,也就是org.springframework.web.context.support.xmlwebapplicationcontext
contextclassname = defaultstrategies.getproperty(webapplicationcontext.
class
.getname());
try
{
return
classutils.forname(contextclassname, contextloader.
class
.getclassloader());
}
catch
(classnotfoundexception ex) {
throw
new
applicationcontextexception(
"failed to load default context class ["
+ contextclassname +
"]"
, ex);
}
}
}
|
上面可以看出其实我们也可以自定义spring web的上下文的,那么怎么去指定我们自定义的上下文呢?答案是通过在 web.xml 中指定 contextclass 参数,因此第一小结结尾时说 contextclass 参数和 contextconfiglocation 很重要~~至于 contextconfiglocation 参数,我们跟进 configureandrefreshwebapplicationcontext 即可看到,如下图:
总结:
spring mvc启动流程大致就是从一个叫 contextloaderlistener 开始的,它是一个servlet监听器,能够被web容器发现并加载,初始化监听器 contextloaderlistener 之后,接着就是根据配置如 contextconfiglocation 和 contextclass 创建web容器了,如果你不指定 contextclass 参数值,则默认创建的spring web容器类型为 xmlwebapplicationcontext ,最后一步就是根据你配置的 contextconfiglocation 文件路径去配置并刷新容器了.
3、 dispatcherservlet 控制器的初始化 。
好了,上面我们简单地分析了spring mvc容器初始化的源码,我们永远不会忘记,我们默认创建的容器类型为 xmlwebapplicationcontext ,当然我们也不会忘记,在 web.xml 中,我们还有一个重要的配置,那就是 dispatcherservlet 。下面我们就来分析下 dispatcherservlet 的初始化过程.
dispatcherservlet ,就是一个servlet,一个用来处理request请求的servlet,它是spring mvc的核心,所有的请求都经过它,并由它指定后续操作该怎么执行,咋一看像一扇门,因此我管它叫“闸门”。在我们继续之前,我们应该共同遵守一个常识,那就是-------无论是监听器还是servlet,都是servlet规范组件,web服务器都可以发现并加载它们.
下面我们先看看 dispatcherservlet 的继承关系:
看到这我们是不是一目了然了, dispatcherservlet 继承了 httpservlet 这个类, httpservlet 是servlet技术规范中专门用于处理http请求的servlet,这就不难解释为什么spring mvc会将 dispatcherservlet 作为统一请求入口了.
因为一个servlet的生命周期是 init() -> service() -> destory() ,那么 dispatcherservlet 怎么初始化呢?看上面的继承图,我们进到 httpservletbean 去看看.
果不其然, httpservletbean 类中有一个 init() 方法, httpservletbean 是一个抽象类, init() 方法如下:
可以看出方法采用 final 修饰,因为 final 修饰的方法是不能被子类继承的,也就是子类没有同样的 init() 方法了,这个 init 方法就是 dispatcherservlet 的初始化入口了.
接着我们跟进 frameworkservlet 的 initservletbean() 方法:
在方法中将会初始化不同于第一小节的web容器,请记住,这个新的spring web 容器是专门为 dispactherservlet 服务的,而且这个新容器是在第一小节根root容器的基础上创建的,我们在 <servlet> 标签中配置的初始化参数被加入到新容器中去.
至此, dispatchersevlet 的初始化完成了,听着有点蒙蔽,但其实也是这样,上面的分析仅仅只围绕一个方法,它叫 init() ,所有的servlet初始化都将调用该方法.
总结
dispactherservlet 的初始化做了两件事情,第一件事情就是根据根web容器,也就是我们第一小节创建的 xmlwebapplicationcontext ,然后创建一个专门为 dispactherservlet 服务的web容器,第二件事情就是将你在web.xml文件中对 dispactherservlet 进行的相关配置加载到新容器当中.
3、每个request调用请求经历了哪些过程 。
其实说到这才是 dispatcherservlet 控制器的核心所在,因为web框架无非就是接受请求,处理请求,然后响应请求。当然了,如果 dispactherservlet 只是单纯地接受处理然后响应请求,那未免太弱了,因此spring设计者加入了许许多多的新特性,比如说拦截器、消息转换器、请求处理映射器以及各种各样的 resolver ,因此spring mvc非常强大.
dispatcherservlet 类不做相关源码分析,因为它就是一个固定的执行步骤,什么意思呢?一个request进来,大致就经历这样的过程:
接受请求 -----> 是否有各种各样的处理器 handler -------> 是否有消息转换器 handleradapter --------> 响应请求 。
上面每一步如果存在相应的组件,当然前提是你在项目中有做相关的配置,则会执行你配置的组件,最后响应请求。因此明白大致的流程之后,如果你想调试一个request,那么你完全可以在 dispatcherservlet 类的 dodispatch 方法中打个断点,跟完代码之后你就会发现其实大致流程就差不多了.
4、后话 。
本文的工程是基于传统的web.xml加载web项目,当然在spring mvc中我们也可以完全基于注解的方式进行配置,我们可以通过实现 webapplicationinitializer 来创建自己的web启动器,也可以通过继承 abstractannotationconfigdispatcherservletinitializer 来创建相应的spring web容器(包括上面说到的根容器和servlet web容器),最后通过继承 webmvcconfigurationsupport 再一步进行自定义配置(相关拦截器,bean等) 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:https://juejin.im/post/5b207dc86fb9a01e49294f42 。
最后此篇关于Spring MVC 启动过程源码分析详解的文章就讲到这里了,如果你想了解更多关于Spring MVC 启动过程源码分析详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
大家好,我是汤师爷~ 什么是订单履约系统? 订单履约是从消费者下单支付到收到商品的全流程管理过程,包括订单接收、订单派单、库存分配、仓储管理和物流配送等环节,核心目标是确保商品准时、准确地送达消费
大家好,我是汤师爷~ 今天聊聊促销系统整体规划。 各类促销活动的系统流程,可以抽象为3大阶段: B端促销活动管理:商家运营人员在后台系统中配置和管理促销活动,包括设定活动基本信息、使用规则
全称“Java Virtual Machine statistics monitoring tool”(statistics 统计;monitoring 监控;tool 工具) 用于监控虚拟机的各种运
主要是讲下Mongodb的索引的查看、创建、删除、类型说明,还有就是Explain执行计划的解释说明。 可以转载,但请注明出处。  
1>单线程或者单进程 相当于短链接,当accept之后,就开始数据的接收和数据的发送,不接受新的连接,即一个server,一个client 不存在并发。 2>循环服务器和并发服务器
详解 linux中的关机和重启命令 一 shutdown命令 shutdown [选项] 时间 选项: ?
首先,将json串转为一个JObject对象: ? 1
matplotlib官网 matplotlib库默认英文字体 添加黑体(‘SimHei')为绘图字体 代码: plt.rcParams['font.sans-serif']=['SimHei'
在并发编程中,synchronized关键字是常出现的角色。之前我们都称呼synchronized关键字为重量锁,但是在jdk1.6中对synchronized进行了优化,引入了偏向锁、轻量锁。本篇
一般我们的项目中会使用1到2个数据库连接配置,同程艺龙的数据库连接配置被收拢到统一的配置中心,由DBA统一配置和维护,业务方通过某个字符串配置拿到的是Connection对象。  
实例如下: ? 1
1. MemoryCahe NetCore中的缓存和System.Runtime.Caching很相似,但是在功能上做了增强,缓存的key支持object类型;提供了泛型支持;可以读缓存和单个缓存
argument是javascript中函数的一个特殊参数,例如下文,利用argument访问函数参数,判断函数是否执行 复制代码 代码如下: <script
一不小心装了一个Redis服务,开了一个全网的默认端口,一开始以为这台服务器没有公网ip,结果发现之后悔之莫及啊 某天发现cpu load高的出奇,发现一个minerd进程 占了大量cpu,googl
今天写这个是为了 提醒自己 编程过程 不仅要有逻辑 思想 还有要规范 代码 这样可读性 1、PHP 编程规范与编码习惯最主要的有以下几点: 1 文件说明 2 funct
摘要:虚拟机安装时一般都采用最小化安装,默认没有lspci工具。一台测试虚拟网卡性能的虚拟机,需要lspci工具来查看网卡的类型。本文描述了在一个虚拟机中安装lspci工具的具体步骤。 由于要测试
1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统
目录 算术运算符 基本四则运算符 增量赋值运算符 自增/自减运算符 关系运算符 逻
如下所示: ? 1
MapperScannerConfigurer之sqlSessionFactory注入方式讲解 首先,Mybatis中的有一段配置非常方便,省去我们去写DaoImpl(Dao层实现类)的时间,这个
我是一名优秀的程序员,十分优秀!