`
xlsky0713
  • 浏览: 3875 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

spring学习笔记之DispatcherServlet源码解读

阅读更多
DispatcherServlet--翻译spring-framework-reference.pdf及源码解读。


最近在看spring源码,同时也看着英文版的spring-framework-reference.pdf。顺便花点时间翻译下来,一方面记录下学习路程,另一方面加深印象。


spring web MVC 框架,就像其他web MVC框架一样,是请求驱动,围绕一个核心的servlet设计,这个servlet把接收的请求(request)分别分发到不同的控制器并提供其他功能促进web应用的开发。而spring的DispatcherServlet做的事情更多。它完全整合了spring Ioc容器方便你使用spring所拥有的功能。

这幅图解释了spring web MVC DispatcherServlet对请求的处理流程。其实DispatcherServlet是采用了前端控制器(Front Controller)的设计模式,这种设计模式也被其他流行的web框架所采用。

DispatcherServlet层次类图

根据类图,知道DispatcherServlet实际上是一个Servlet,因为它继承于HttpServlet,所以它需要在web.xml定义,并且在web.xml定义需要通过DispatherServlet处理的请求的URL映射。

这是标准的J2EE servlet 配置方式。下面是一个例子:

view plaincopy to clipboardprint?

    <web-app> 
        <servlet> 
            <servlet-name>example</servlet-name> 
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
            <load-on-startup>1</load-on-startup> 
        </servlet> 
        <servlet-mapping> 
            <servlet-name>example</servlet-name> 
            <url-pattern>*.form</url-pattern> 
        </servlet-mapping> 
    </web-app> 


       上面这个例子,所有以.form结尾的请求都会被名为example的DispaterServlet处理。这是使用spring MVC的第一步,一系列beans需要配置。

       在spring web MVC框架,每一个DispatcherServlet都有它自己的WebApplicationContext,这些WebApplicationContext继承了一个根WebApplicationContextde定义的所有bean。这些被继承的bean可以在具体的servlet中改写作用域,也可以在一个servlet实例定义新的局部的作用域bean。

       框架在启动初始化DispatcherServlet时,在WEB应用下的WEB-INF目录下寻找名为[servlet-name]-servlet.xml的文件,并创建定义的bean,这些bean如果同样在全局作用域中定义(名字相同),则覆盖全局bean.如在上一例中,则会找WEB-INF/example-servlet.xml文件。当然,配置文件的路径可以通过servlet的参数配置。

       WebApplicationContext是普通的ApplicationContext的扩展,它增加了一些web应用需要的特性。比如,它解析themes与ApplicationContext是不同的,因为它会关联到ServletContext。WebApplicationContext绑定到了ServletContext,在你需要访问它时,只需要用RequestContextUtils类的静态方法就可以找到它。

       DispatcherServlet有几个特殊的bean用来处理request请求和渲染适合的视图。这些bean包含在spring框架中,并可在WebApplicationContext像其他普通bean一样配置。在下面有更具体的介绍。

Bean类型


说明

Controllers


MVC架构的'C'

Handler mappings


主要负责预处理,后置处理等一系列执行处理和满足特定条件下的控制器的执行,比如URL匹配等

View resolvers


负责根据视图名称解析视图

Locale resolver


负责语言本地化

Theme resolver


负责根据应用使用解析主题,比如可以定制个性化版面

multipart file


resolver




负责在html页面提供文件上传功能

Handler exception resolver(s)


负责提供处理异常功能

DispatcherServlet服务启动,一个请求到特定的DispatcherServlet,这时Dispatcher开始处理请求。下面是DispatcherServlet处理请求的完整过程。

1.寻找WebApplicationContext并且在request作为一个属性绑定方便控制器和其他成员使用。默认的绑定KEY是  DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE.

2.本地化解析器绑定到request中。如果没有使用该解析器,则不作任何事情。

3.主题解析器绑定到request中。如果没有使用该解析器,则不做任何事情。

4.如果multipart resolver被指定,则request会检测它。如果找到,request被包装成MultipartHttpServletRequest方便之后的其他成员处理。

5.寻找合适的的处理模块。如果找到,该处理模块相关的执行链如预处理,后置处理和控制器等都会被执行为模块的渲染作准备。

6.如果一个模块返回,则会渲染一个视图。如果没有模块返回,就没有视图被渲染,因为请求可能已经处理完成。

处理请求过程中抛出异常,请求会启用在WebApplicationContext声明的异常处理解析器。在发生异常时,可以使用这些解析器定义用户的行为。

DispatcherServlet也支持返回last-modification-date。DispatcherServlet首先寻找一个合适的处理映射并检测这处理模块是否实现了LastModified接口。如果是,这个接口的方法long getLastModified(request)返回值给客户端。

你也可以定制DispatcherServlet,通过在web.xml或者servlet初始参数上添加上下文容器的参数。具体如下:

参数


说明

contextClass


实现了WebApplicationContext接口的类,用来初始上下文环境。如果没有指定,则默认XmlWebApplicationContext

contextConfigLocation


传递给context实例的字符串,说明哪里可以找到context。这个字符串可被逗号拆分从而支持多个contexts。

namespace


WebApplicationContext命名空间。默认是 [servlet-name]-servlet



现在再查看源码加深理解。

首先服务器如tomcat启动时,就会将web.xml定义的context的bean都会实例化。入口当然是

org.springframework.web.servlet.DispatcherServlet。它是一个HttpServlet,所以必然会执行init()方法。我们查看根据上述的DispatcherServlet类图查看,发现HttpServletBean才有init()方法。

view plaincopy to clipboardprint?

    public final void init() throws ServletException { 
            if (logger.isDebugEnabled()) { 
                logger.debug("Initializing servlet '" + getServletName() + "'"); 
            } 
            // Set bean properties from init parameters. 
            try { 
                PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); 
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); 
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader)); 
                initBeanWrapper(bw); 
                bw.setPropertyValues(pvs, true); 
            } 
            catch (BeansException ex) { 
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); 
                throw ex; 
            } 
            // Let subclasses do whatever initialization they like. 
            initServletBean(); 
            if (logger.isDebugEnabled()) { 
                logger.debug("Servlet '" + getServletName() + "' configured successfully"); 
            } 
        } 

在这个方法里最重要的是initServletBean()。它会调用FrameworkServlet下的initServletBean(),这个方法改写了HttpServletBean的initServletBean()。代码如下:


view plaincopy to clipboardprint?

    protected final void initServletBean() throws ServletException, BeansException { 
            getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); 
            if (this.logger.isInfoEnabled()) { 
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); 
            } 
            long startTime = System.currentTimeMillis(); 
            try { 
                this.webApplicationContext = initWebApplicationContext(); 
                initFrameworkServlet(); 
            } 
            catch (ServletException ex) { 
                this.logger.error("Context initialization failed", ex); 
                throw ex; 
            } 
            catch (BeansException ex) { 
                this.logger.error("Context initialization failed", ex); 
                throw ex; 
            } 
            if (this.logger.isInfoEnabled()) { 
                long elapsedTime = System.currentTimeMillis() - startTime; 
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + 
                        elapsedTime + " ms"); 
            } 
        } 

我们应该注意到这个方法有修饰符final,是不能子类重写的,这就体现了 “开-闭原则(open for extension,close for modification)”我们再看initWebApplicationContext() 。

view plaincopy to clipboardprint?

    protected WebApplicationContext initWebApplicationContext() throws BeansException { 
            WebApplicationContext wac = findWebApplicationContext(); 
            if (wac == null) { 
                // No fixed context defined for this servlet - create a local one. 
                WebApplicationContext parent = 
                        WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 
                wac = createWebApplicationContext(parent); 
            } 
            if (!this.refreshEventReceived) { 
                // Apparently not a ConfigurableApplicationContext with refresh support: 
                // triggering initial onRefresh manually here. 
                onRefresh(wac); 
            } 
            if (this.publishContext) { 
                // Publish the context as a servlet context attribute. 
                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; 
        } 

FindWebApplicationContext() 方法是从 ServletContext 属性取 WebApplicationContext ,如果没有配置 ServletContext 属性的话,则返回 null 。这时,先取回根 WebApplicationContext,key 是 WebApplicationContext. ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 。



而 createWebApplicationContext(parent) 方法是真正得到我们 DispatcherServlet 对应的 WebApplicationContext 。


view plaincopy to clipboardprint?

    protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) 
                throws BeansException { 
            if (this.logger.isDebugEnabled()) { 
                this.logger.debug("Servlet with name '" + getServletName() + 
                        "' will try to create custom WebApplicationContext context of class '" + 
                        getContextClass().getName() + "'" + ", using parent context [" + parent + "]"); 
            } 
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) { 
                throw new ApplicationContextException( 
                        "Fatal initialization error in servlet with name '" + getServletName() + 
                        "': custom WebApplicationContext class [" + getContextClass().getName() + 
                        "] is not of type ConfigurableWebApplicationContext"); 
            } 
            ConfigurableWebApplicationContext wac = 
                    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass()); 
            wac.setParent(parent); 
            wac.setServletContext(getServletContext()); 
            wac.setServletConfig(getServletConfig()); 
            wac.setNamespace(getNamespace()); 
            wac.setConfigLocation(getContextConfigLocation()); 
            wac.addApplicationListener(new SourceFilteringListener(wac, this)); 
            postProcessWebApplicationContext(wac); 
            wac.refresh(); 
            return wac; 
        } 



在这个方法里,首先调用 getContextClass() 获取默认的 ContextClass, 就是 public static final Class DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext. class 。


view plaincopy to clipboardprint?

    ConfigurableWebApplicationContext  wac  = 
    (ConfigurableWebApplicationContext) BeanUtils. instantiateClass (getContextClass());  

这行代码就是利用 XmlWebApplicationContext 的无参数构造函数实例化 XmlWebApplicationContext , 因为 XmlWebApplicationContext 实现了 ConfigurableWebApplicationContext 接口。接下来就是填充 XmlWebApplicationContext 的属性,这些属性部分在上面有提及过。这样在这个 WebApplicationContext 就可以使用了。
分享到:
评论

相关推荐

    spring学习笔记,包括源码学习

    这个"spring学习笔记,包括源码学习"的资料很可能包含了一系列关于Spring框架的核心概念、配置、使用方法以及深入源码的解析。 首先,让我们来了解一下Spring框架的基础知识。Spring的核心特性是依赖注入,它允许...

    Spring高级源码学习笔记.zip

    源码学习是提升编程技能的重要途径,尤其是在理解复杂框架如Spring的工作原理时。本笔记旨在深入解析Spring的高级源码,帮助程序员从应用层面过渡到源码级的理解。 Spring的核心组件包括Bean容器、AOP代理、数据...

    Spring笔记示例源代码

    "Spring笔记示例源代码"这个资源很可能是为了帮助学习者深入理解Spring框架的各种功能和用法而提供的实际代码示例。 1. **Spring IoC**:IoC是Spring的核心特性,它将对象的创建和管理权交给Spring容器,使得开发者...

    spring学习文档及源码笔记

    这个资源包含的"spring学习文档及源码笔记"是深入理解并掌握 Spring MVC 的宝贵资料。下面,我们将详细探讨 Spring MVC 的核心概念、工作原理以及如何利用它来构建 Web 应用。 1. **Spring MVC 架构** - **...

    Spring 学习笔记二

    在本篇"Spring 学习笔记二"中,我们将深入探讨Spring框架的核心概念和技术细节,以便更好地理解和使用这个广泛应用于企业级Java开发的框架。Spring以其依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-...

    Spring学习笔记系列之三

    本篇我们将聚焦于"Spring学习笔记系列之三"中的关键知识点——SpringMVC的源码分析,特别是父子容器的启动原理。这个主题是理解Spring MVC工作流程、定制化配置以及优化应用程序性能的关键。 首先,我们要明白...

    Spring 学习笔记一

    学习 Spring 源码可以帮助开发者更深入地理解其工作原理。例如,了解 BeanFactory 和 ApplicationContext 如何管理 Bean 的生命周期,以及如何实现依赖注入。此外,研究 Spring AOP 的底层实现,如代理模式的应用,...

    Spring技术内幕 学习笔记

    《Spring技术内幕 学习笔记》是一份深入探讨Spring框架核心机制的学习资料,结合作者zzc1684在iteye博客上的博文,我们可以从中学习到Spring框架的多个重要知识点。Spring作为Java企业级应用开发的基石,其设计思想...

    spring5源码分析笔记

    Spring框架是Java开发中最常用的轻量级开源框架之一,它为构建企业级应用程序...以上是Spring5源码分析笔记中的主要知识点,深入学习这些内容将有助于开发者更好地理解和利用Spring框架,从而提高开发效率和应用质量。

    Spring MVC 学习笔记 一 创建项目

    **Spring MVC 学习笔记 一 创建项目** 在IT领域,Spring MVC是Java Web开发中的一个强大框架,它简化了构建交互式Web应用程序的过程。本笔记将深入探讨如何创建一个基本的Spring MVC项目,让我们开始吧。 首先,...

    SpringMVC学习笔记+学习源码.zip

    总的来说,这份"SpringMVC学习笔记+学习源码.zip"资源涵盖了SpringMVC的基本概念、配置、控制器、数据绑定、异常处理、视图解析等多个方面的内容,对于初学者和有一定经验的开发者都是很好的学习资料。通过深入学习...

    Spring MVC 学习笔记

    **Spring MVC 学习笔记** Spring MVC 是 Spring 框架的一个模块,专门用于构建 Web 应用程序。它提供了一种模型-视图-控制器(MVC)架构,简化了开发过程,使得开发者可以专注于业务逻辑而不必过于关注底层的细节。...

    spring5源码笔记.zip

    Spring 5 源码分析笔记 Spring框架作为Java企业级应用开发的基石,其设计理念、模块架构以及核心功能在业界都有着广泛的应用。Spring 5是该框架的一个重要版本,引入了许多新特性和性能优化,使得它更加现代化,...

    Spring MVC学习笔记MD.7z

    这个压缩包“Spring MVC学习笔记MD.7z”包含了作者在学习Spring MVC过程中的笔记、源代码以及相关文档,非常适合初学者或希望深入理解Spring MVC的开发者。 首先,`SpringMVC-Study.7z` 可能是作者整理的Spring MVC...

    Spring3学习笔记题记

    《Spring3学习笔记题记》 在IT领域,Spring框架是Java开发中不可或缺的一部分,尤其在企业级应用中,Spring以其强大的功能和灵活性备受青睐。本文将深入探讨Spring3版本的相关知识点,帮助开发者更好地理解和使用这...

    spring MVC学习笔记

    **Spring MVC 学习笔记** Spring MVC 是 Spring 框架的一个模块,专门用于构建 Web 应用程序。它提供了一种模型-视图-控制器(Model-View-Controller)架构,帮助开发者处理请求、控制应用程序流程,并实现业务逻辑...

    Spring MVC 学习笔记(一)

    **Spring MVC 学习笔记(一)** Spring MVC 是 Spring 框架的重要组成部分,它是一个用于构建 Web 应用程序的模型-视图-控制器(MVC)框架。Spring MVC 通过解耦应用程序的不同部分,使开发人员能够更轻松地进行...

    spring使用与源码查看笔记

    本笔记将探讨Spring的使用方法以及如何深入理解其源码,帮助开发者更好地掌握这个广泛使用的框架。 一、Spring框架基础 1. **依赖注入(DI)**:Spring的核心特性之一,它允许对象之间的依赖关系在运行时被管理,...

    Spring MVC 学习笔记 十二 PDF/Excel格式输出

    本学习笔记主要围绕Spring MVC的使用、配置和核心组件进行深入探讨,旨在帮助开发者更好地理解和掌握这一框架。 在Spring MVC中,Model代表业务逻辑和数据,View负责数据的展示,而Controller处理用户请求,协调...

Global site tag (gtag.js) - Google Analytics