SpringMVC源代码深度分析DispatcherServlet核心的控制器(初始化)
SpringMVC是非常优秀的MVC框架,每一个框架都是为了我们提高开发效率,我们试图通过对SpringMVC的源码去了解这个框架,了解整个设计思想,框架要有扩展性,这里用的比較多是接口和抽象,是框架的主力,我们通过了解源码能对SpringMVC框架更了解,也能对我们开发思想有非常大的启示。
SpringMVC由几个核心类和接口组成的。我们今天要的一个是DispatcherServlet核心的前置控制器。配置在Web.xml中。所以请求都经过它来统一分发的。SpringMVC几个核心类和接口都会出如今DispatcherServlet的源代码中,我这里大概介绍一个。今天重点是介绍DispatcherServlet核心的前置控制器。后面我们在具体分析其他的几个核心类和接口分析。
DispatcherServlet的继承关系图,能清晰的了解整个层次。
当Web项目启动时。做初始化工作。所以我们大部分是配置在Web.xml里面,这样项目一启动,就会运行相关的初始化工作,以下是Web.xml代码:
<servlet>
<servlet-name>SpringMVCDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-mvc.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVCDispatcher</servlet-name>
<url-pattern>*.jhtml</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HessianDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:hessian-service.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HessianDispatcher</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
这里配置了两个DispatcherServlet,后面会介绍到。怎么各自处理,有各自的上下文容器。
load-on-startup是启动的优先级,spring-mvc.xml是我们配置bean的一些信息
最早我们開始学习MVC结构时。就是学servlet,都是继 承了HttpServlet 类,也是又一次了init、doGet、doPost、destroy方法,我这边就不介绍HttpServlet 类,DispatcherServlet也是间接最高继承了HttpServlet,如图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29uZ2Nvbmc2OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
我们先了解项目启动,DispatcherServlet和父类都做了什么事情呢?这是我们今天的重点。
第一步:DispatcherServlet继承了FrameworkServlet,FrameworkServlet继承了HttpServletBean,HttpServletBean继承了HttpServlet 类,而HttpServletBean类有一个入口点就是重写了init方法。如图所看到的:
init方法做了什么事情呢?接下来我们来详细分析:
init方法里有涉及到了BeanWrapper。PropertyValues,ResourceLoader。我这里大概介绍一下。
1)PropertyValues:获取Web.xml里面的servlet的init-param(web.xml)
/**
* Create new ServletConfigPropertyValues.
* @param config ServletConfig we'll use to take PropertyValues from
* @param requiredProperties set of property names we need, where
* we can't accept default values
* @throws ServletException if any required properties are missing
*/
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException {
Enumeration en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String property = (String) en.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value));
}
}
说明:
Enumeration en = config.getInitParameterNames();
获取了init-param的param-name和param-value值,并设置配置參数到PropertyValue,如图所看到的:
2)BeanWrapper:封装了bean的行为,提供了设置和获取属性值。它有相应的BeanWrapperImpl。如图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29uZ2Nvbmc2OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
3)ResourceLoader:接口仅有一个getResource(String location)的方法。能够依据一个资源地址载入文件资源。classpath:这样的方式指定SpringMVC框架bean配置文件的来源。
ResourcePatternResolver扩展了ResourceLoader接口。获取资源
ResourcePatternResolver
resolver =new PathMatchingResourcePatternResolver();
resolver.getResources("classpath:spring-mvc.xml");
总结:
先通过PropertyValues获取web.xml文件init-param的參数值,然后通过ResourceLoader读取.xml配置信息,BeanWrapper对配置的标签进行解析和将系统默认的bean的各种属性设置到相应的bean属性。
在init方法里还调用了initServletBean();这里面又实现了什么。HttpServletBean在为子类提供模版、让子类依据自己的需求实现不同的ServletBean的初始化工作。这边是由HttpServletBean的子类FrameworkServlet来实现的,如图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29uZ2Nvbmc2OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
this.webApplicationContext = initWebApplicationContext();初始化SpringMVC 上下文容器。servlet的上下文容器是ServletContext。对initWebApplicationContext()。进行跟踪,查看这种方法做了什么事情?
protected WebApplicationContext initWebApplicationContext() {
//根节点上下文,是通过ContextLoaderListener载入的,server启动时,最先载入的
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
//要对上下文设置父上下文和ID等
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//Servlet不是由编程式注冊到容器中,查找servletContext中已经注冊的WebApplicationContext作为上下文
if (wac == null) {
wac = findWebApplicationContext();
}
//假设都没找到时,就用根上下文就创建一个上下文有ID
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
//在上下文关闭的情况下调用refesh可启动应用上下文,在已经启动的状态下。调用refresh则清除缓存并又一次装载配置信息
if (!this.refreshEventReceived) {
onRefresh(wac);
}
//对不同的请求相应的DispatherServlet有不同的WebApplicationContext、而且都存放在ServletContext中
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
} return wac;
}
总结:
initWebApplicationContext初始化上下文。并作为值放到了ServletContext里,由于不同的DispatherServlet有相应的各自的上下文,并且上下文有设置父上下文和id属性等。上下文项目启动时会调用createWebApplicationContext()方法,如图所看到的:
然后会初始化,设置设置父上下文和id属性等。如图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29uZ2Nvbmc2OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
1)获取ContextLoaderListener载入的上下文并标示为跟上下文,假设是编程式传入。没初始化,以根节点为父上文。并设置ID等信息。然后初始化。
2)假设上下文是为空的,Servlet不是由编程式注冊到容器中,查找servletContext中已经注冊的WebApplicationContext作为上下文,假设都没找到时,就用根上下文就创建一个上下文有ID。在上下文关闭的情况下调用refesh可启动应用上下文,在已经启动的状态下,调用refresh则清除缓存并又一次装载配置信息
3)对不同的请求相应的DispatherServlet有不同的WebApplicationContext、而且都存放在ServletContext中。以servlet-name为key保存在severtContext。前面有配置了两个DispatherServlet,都有各自的上下文容器。如图所看到的:
回调函数onRefresh还做了一些提供了SpringMVC各种编程元素的初始化工作。 onRefresh在为子类提供模版、让子类依据自己的需求实现不同的onRefresh的初始化工作。这边是由FrameworkServlet的子类DispatcherServlet来实现的,如图所看到的:
我们如今来分析SpringMVC组件进行初始化。并封装到DispatcherServlet中
//初始化上传文件解析器
initMultipartResolver(context);
//初始化本地解析器
initLocaleResolver(context);
//初始化主题解析器
initThemeResolver(context);
//初始化映射处理器
initHandlerMappings(context);
//初始化适配器处理器
initHandlerAdapters(context);
//初始化异常处理器
initHandlerExceptionResolvers(context);
//初始化请求到视图名翻译器
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
我们这边拿几个比較基本的分析一下详细实现了什么。
第一:initHandlerMappings初始化映射处理器
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
说明:
1)detectAllHandlerMappings默认是true,依据类型匹配机制查找上下文及父容器上下文中全部类型为HandlerMapping的bean,将它们作为该类型组件。并放到ArrayList<HandlerMapping>中。
2)detectAllHandlerMappings假设是false时。查找key为handlerMapping的HandlerMapping类型的bean为该类组件,并且 Collections.singletonList仅仅有一个元素的集合。
3)List<HandlerMapping> 是为空的话,使用BeanNameUrlHandleMapping实现类创建该类的组件。
第二:initHandlerAdapters适配器处理器
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null; if (this.detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
OrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
}
}
}
initHandlerAdapters适配器处理器初始化原理跟initHandlerMappings初始化映射处理器一样
这里就不在说明了。
总结:
1)initHandlerMapping会初始化了handlerMethods请求方法的映射。HandlerMapping是处理请求的映射的如图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29uZ2Nvbmc2OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
今天先讲SpringMVC的初始化。当DispatcherServlet初始化后,就会自己主动扫描上下文的bean,依据名称或者类型匹配的机制查找自己定义的组件,找不到则使用DispatcherServlet。Properties定义默认的组件
总结:
HttpServletBean、FrameworkServlet、DispatcherServlet三个不同的类层次,SpringMVC对三个以抽象和继承来实现不用的功能。分工合作。实现了解耦的设计原则。
我们在回想一下。各自做了什么事情。HttpServletBean是实现了获取web.xml中的<init-param>配置元素的值。FrameworkServlet实现了SpringMVC上下文并依据不同的DispatcherServlet放在以servlet-name为key值severtContext中。DispatcherServlet主要实现了初始化SpringMVC组件元素。
SpringMVC源代码深度分析DispatcherServlet核心的控制器(初始化)的更多相关文章
- springMVC源码分析--DispatcherServlet请求获取及处理
在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...
- SpringMVC 源代码深度解析 IOC容器(Bean 解析、注册)
SpringMVC通过一个配置文件描述Bean以及Bean之间的依赖关系,利用Java的反射机制实例化Bean并建立Bean之间的依赖关系.IOC容器在完成这些底层工作的基础还提供了Bean的实例缓. ...
- SpringMVC源码解析-DispatcherServlet启动流程和初始化
在使用springmvc框架,会在web.xml文件配置一个DispatcherServlet,这正是web容器开始初始化,同时会在建立自己的上下文来持有SpringMVC的bean对象. 先从Dis ...
- [心得体会]SpringMVC源码分析
1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...
- SpringMVC源码分析(3)DispatcherServlet的请求处理流程
<springmvc源码分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的 ...
- 2.SpringMVC源码分析:DispatcherServlet的初始化与请求转发
一.DispatcherServlet的初始化 在我们第一次学Servlet编程,学java web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根 ...
- springmvc 源码分析(二)-- DiapartcherServlet核心调用流程分析
测试环境搭建: 本次搭建是基于springboot来实现的,代码在码云的链接:https://gitee.com/yangxioahui/thymeleaf.git 项目结构代码如下: 一: cont ...
- springMVC源码分析--容器初始化(二)DispatcherServlet
在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...
- SpringMVC源码阅读:核心分发器DispatcherServlet
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...
随机推荐
- cocos2d-x环境搭建(win7+cocos2d-x-3.0)
一.环境需准备的材料: 1.VS2012,下载地址:官网 2.cocos2d-x和cocostudio,下载地址:官网 3.eclispe,我用的是:adt-bundle-windows-x86_64 ...
- 学习NodeJS第一天:node.js介绍
Node.JS 前辈 C 程序猿 Ryan Dahl(http://four.livejournal.com/)工程,根据 Google 著名的开源 JavaScript 发动机 V8 对于二次开发 ...
- 【剑指offer】面试题35:第一个数字只出现一次
def FirstNotRepeatingChar(string): hashStr = [0] * 256 for c in string: hashStr[ord(c)] += 1 for c i ...
- 黄聪:Microsoft Enterprise Library 5.0 系列教程(八) Unity Dependency Injection and Interception
原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(八) Unity Dependency Injection and Interception 依赖注入容器Uni ...
- 445port入侵详细解释
445port入侵具体解释 关于"445port入侵"的内容445port入侵具体解释本站搜索很多其它关于"445port入侵"的内容 445port入侵, ...
- Ubuntu 上 hi3531 交叉编译环境 arm-hisiv100nptl-linux 建设过程
安装SDK 1.Hi3531 SDK包的位置 在"Hi3531_V100R001***/01.software/board"夹,你可以看到一个 Hi3531_SDK_Vx. ...
- [原创]如何编写多个阻塞队列连接下的多生产者多消费者的Python程序
平常在写程序时,往往会遇到一个需求:在程序的多个阶段都会出现阻塞的可能,因此,这多个阶段就需要并发执行. Python的多线程有一个特点,就是不允许从外部结束一个运行中的线程,这给我们编写代码时带来了 ...
- bzoj1001/BJOI2006 灰太狼抓到的兔子
1001: [BeiJing2006]狼抓兔子(id=1001">传送门) 图论新知识..没学过.. 平面图最小割等于对偶图的最短路 详见课件:http://wenku.baidu.c ...
- 解决SMARTFORMS 中table 控件单行跨页的问题
在CX项目中,MM模块做了大量的的单据打印的工作,一个问题困扰了我好久,一直不能解决.当物料描述很长时,table控件在单元格中能自动换行,这样就有可能在换页处出现一行记录的一部分打在上一页,一部分记 ...
- c#并行任务多种优化方案分享(异步委托)
遇到一个多线程任务优化的问题,现在解决了,分享如下. 假设有四个任务: 任务1:登陆验证(CheckUser) 任务2:验证成功后从Web服务获取数据(GetDataFromWeb) 任务3:验证成功 ...