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核心的控制器(初始化)的更多相关文章

  1. springMVC源码分析--DispatcherServlet请求获取及处理

    在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...

  2. SpringMVC 源代码深度解析 IOC容器(Bean 解析、注册)

    SpringMVC通过一个配置文件描述Bean以及Bean之间的依赖关系,利用Java的反射机制实例化Bean并建立Bean之间的依赖关系.IOC容器在完成这些底层工作的基础还提供了Bean的实例缓. ...

  3. SpringMVC源码解析-DispatcherServlet启动流程和初始化

    在使用springmvc框架,会在web.xml文件配置一个DispatcherServlet,这正是web容器开始初始化,同时会在建立自己的上下文来持有SpringMVC的bean对象. 先从Dis ...

  4. [心得体会]SpringMVC源码分析

    1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...

  5. SpringMVC源码分析(3)DispatcherServlet的请求处理流程

    <springmvc源码分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的 ...

  6. 2.SpringMVC源码分析:DispatcherServlet的初始化与请求转发

    一.DispatcherServlet的初始化 在我们第一次学Servlet编程,学java web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根 ...

  7. springmvc 源码分析(二)-- DiapartcherServlet核心调用流程分析

    测试环境搭建: 本次搭建是基于springboot来实现的,代码在码云的链接:https://gitee.com/yangxioahui/thymeleaf.git 项目结构代码如下: 一: cont ...

  8. springMVC源码分析--容器初始化(二)DispatcherServlet

    在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...

  9. SpringMVC源码阅读:核心分发器DispatcherServlet

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...

随机推荐

  1. Context Switch and System Call

    How many Context Switches is “normal”? This depends very much on the type of application you run. If ...

  2. Unity3D开发一个2D横版射击游戏

    教程基于http://pixelnest.io/tutorials/2d-game-unity/ , 这个例子感觉还是比较经典的, 网上转载的也比较多. 刚好最近也在学习U3D, 做的过程中自己又修改 ...

  3. CentOS 6.5安全加固及性能优化

    (文章来自:http://www.cnblogs.com/seasonzone/p/3526296.html) 我们可以通过调整系统参数来提高系统内存.CPU.内核资源的占用,通过禁用不必要的服务.端 ...

  4. Oracle ORA-01034,ORA-27101,ORA-00600

    本机IP地址:192.168.1.163 [oracle@rtest ~]$ sqlplus /nolog SQL*Plus: Release 10.2.0.2.0 - Production on S ...

  5. Automatically generate serial number in abap

    流水号的产生:   在ABAP开发中,很多地方都需要按照自己的规则生成流水号,把这些流水号保存进透明表,作为唯一标示. 一. 系统中设定流水号 使用T-Code SNRO来创建一个流水号标识对象. 输 ...

  6. CodeForces 52C Circular RMQ(间隔周期段树,间隔更新,间隔总和)

    转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://codeforces.com/problemset/problem/52/C You are g ...

  7. HDOJ 2665 Kth number

    静态区间第K小....划分树裸题 Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K ( ...

  8. 看PHP在内部迭代的动作

    以下我们来了解怎样实现一个自己定义的迭代器,然后再開始慢慢理解迭代器的内部工作原理.先来看一个官方的样例: <? php class myIterator implements Iterator ...

  9. 银行家算法java实现

    关于银行家算法的理论知识,课本或者百度上有好多资料,我就不再多说了,这里把我最近写的银行家算法的实现带码贴出来. 由于这是我们的一个实验,对系统资源数和进程数都指定了,所以这里也将其指定了,其中系统资 ...

  10. 用golang写的 分解x86 intel boot/recovery工具

    源代码地址: https://github.com/sndnvaps/pack-unpack-intel