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. ThinkPHP实现用户登录

    ThinkPHP实现用户登录 ? //默认url入口 ? class IndexAction extends Action{ function index() { //检查用户是否登录 if(isse ...

  2. hadoop-ha组态

    HADOOP HA组态 hadoop2.x的ha组态.这份文件是在那里的描述中hdfs与yarn的ha组态. 这份文件的假设是zk它已被安装并配置,事实上,任何安装. hdfs ha组态 首先.配置c ...

  3. Android数据库专家秘籍(七)经验LitePal查询艺术

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/40153833 经过了多篇文章的学习,我们已经把LitePal中的绝大部分内容都掌握 ...

  4. uva 10192 Vacation(最长公共子)

    uva 10192 Vacation The Problem You are planning to take some rest and to go out on vacation, but you ...

  5. Silverlight之 xaml布局

    目标:在两周内完成一个界面的功能 第1阶段:完成xaml的布局 准备:视频4-14节 第2阶段: 完成环状图 柱状图 TreeView样式 准备: 矢量绘图  telerik 自定义控件  自定义控件 ...

  6. js实现文字逐个显示

    先把代码摆上了吧: <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtm ...

  7. leetcode——Evaluate Reverse Polish Notation 求算式值(AC)

    Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...

  8. hdu 2899 hdu 3400 三分/几何

    hdu2899 : 水提,直接三分,事实上求导后二分也能够. #include<iostream> #include<cstdio> using namespace std; ...

  9. 【DataStructure】The description of Java Collections Framework

    The Java Connections FrameWork is a group of class or method and interfacs in the java.util package. ...

  10. poj 2935 Basic Wall Maze

    是一个图论的基础搜索题- 没什么好说的就是搜索就好 主要是别把 代码写的太屎,错了不好找 #include<cstdio> #include<algorithm> #inclu ...