白话MVC(五)初窥Spring MVC

struts2 目写完 fastupload 文件上 传处 理插件之后, fastupload 目的开 工作 暂告一个段落。因为 struts2框架中文件上传处理的子框架内限定了处理 multipart/form-data请求时,一定要借用临时文件,这不但限制了 struts2框架在处理文件上传的可扩展能力,也使 struts2处理 multipart/form-data请求时非常的低效。

为此, fastupload开发小组向 struts2开发小组提出了重构 struts2中文件上传小框架的提议,以达到易于编写插件,支持高效的流处理和内存处理 multipart/form-data请求的能力, struts2框架的使用者能使用功能更加强大的文件上传组件。这个讨论的过程很漫长,不过最终, struts2开发小组接受了 fastupload开发小组的提议。这个子框架由 fastupload开发小组来开发,不久, struts2的新版本中将会具有一个全新的文件上传框架。

另外一个好消息是 fastupload开发小组另外一个主要志愿者,大名叫 lkc,至今不知道他的真名,已经为 Spring MVC写好了插件,这个插件的一大亮点是可以根据上传的文件大小,来选择合适的解析方式,这个让开发者在 Spring MVC中处理文件上传更得心应手。现在正在准备文档阶段,不久即可发布。

 

好了,提前透露了这么多小道消息,回到今天要干的事情的正题,在为 struts2写插件时,为了弄清 struts2框架中 Model是如何装配的,把 struts2中的大部分代码看了一遍,基本上搞清了 struts2框架中 Model的处理模式和过程。为 Spring MVC写插件,也是 fastupload项目的一个重要目标,虽然这个插件已基本完成,但 Spring MVC作为一个应用最广泛的 MVC框架,不去了解一下其内部,怎么能甘心呢?用技术折腾自己,是码农的最大快乐之一,所以对于 SpringMVC框架,也要弄清楚这 Model层是如何自动装配的,以及其一些内部机制。

因为 Spring MVC的功能实在是太强大,你想到它早已经帮你实现了,你未想到的,它也提前帮你实现了, Spring MVC框架的体积本身不大,但它的内部结构却像是一只经过精心设计的、各部件相互协作、一起运行的瑞士手表。那么这只瑞士手表内部到底是个啥样子,只有深入进去一看究竟。在探索的过程中,做了一些笔记,拿出来和大家分享,希望对想深入了解 Spring MVC的网友有帮助。

本人对 Spring MVC的了解也只是刚刚开始,难免存在认识上的不足,如果这些笔记中有错误的地方,请大家指正。

DispatcherServlet

Spring Web MVC框架,像 多其他 WEB MVC 框架一 驱动 围绕中心 servlet设计,派发请求给控制器,并提供其他的功能性,促进 Web应用的开发。然而 Spring DispatcherServlet做的远不止这些。它完全的集成了 Spring IoC容器,所以能让你使用 Spring框架所有的其他功能。

 

Spring Web MVC 求的 理流程可以从下 中得到 述。 谙设计 模式的 者,会 DispatcherServlet 是前端 模式的一种表现形式,这是 Spring Web MVC与其他主流 web框架分享出的一个设计模式。顺便插一句,虽然 struts2框架中使用过滤器来拦截所有的请求并把请求“揉合”到拦截器和 Action处理,但是其结构也是采用了前端模式。

 

Spring中, ApplicationContext是有作用范 的,在 Spring Web MVC框架中,每个 DispatcherServlet都有其各自的 WebApplicationContext,其 承了根 WebApplicationContext 中定 的所有的 bean 些被 承的 bean 可以在 servlet 的生命周期内被覆盖,也可以定 一个新的范 围的 bean 定的 servlet 例之中。

 

WebApplicationContext 是从 ApplicationContext 拓展而来,具 Web 用必要的功能,不同于普通的 ApplicationContext ,它具有解决主 的能力,知道哪个 servlet 和其相关,它 定到 ServletContext ,在需要 WebApplicationContext 候,通 RequestContextUtils 类总 是能 得它。

WebApplicationContext中特 Bean

Spring DispatcherServlet 使用一些特 bean 求、渲染合适的 视图。这些 bean Spring MVC的一部分。通过在 WebApplicationContext中配置它们之间的一个或者多个,选择哪一个特定的 bean来使用。只不过,你不需要初始化它们,因为在未配置的情况下, Spring MVC维护了一个默认的 bean列表,在探讨这些之前,首先来看一下 DispatcherServlet所依赖的 bean

类型

描述

HandlerMapping

映射传入的请求到处理器,和一系列拦截器去处理,这个 bean只做映射定义功能,具体请求是由 HandlerAdapter完成。目前最流行的 HandlerMapping实现支持注解,以前配置定义方式还仍然被支持。

HandlerAdapter

帮助 DispatcherServlet调用一个处理器映射请求,不管处理时是否被真正调用。例如,调用注解控制器需要先解析多个注解,因此, HandlerAdapter的主要目的是保护 DispatcherServlet,从这些细节中脱离出来。

HanlderExceptionResolver

映射异常到一个视图,容许更复杂的异常处理。

ViewResolver

解决字符串基于的逻辑视图到实际视图类型的映射

ThemeResolver

提供 web应用主题功能,比如个性化主题布局

LocaleResolver

提供客户端本地化,以是 Web应用提供国际化视图

MultipartResolver

解析 multipart/form-data请求,提供表单文件上传的功能

FlashMapManager

保存和检索 FlashMap中的输入输出,用于传递属性到另外一个请求,通常用在 redirect请求的时候。

DispatcherSevlet 配置

前面提到 过,对于 每个特 bean DispatcherServlet 维护 了一个默 实现列表,这些信息保存在 org.springframework.web.servlet包下的 DispatcherServlet.properties文件中。

所有特别的 bean自身都有一些合理的默认值。但是也可以通过配置来改变这些默认的值,比如,为 InternalResourceViewResolver设置 prefix属性,指定 view文件的父路径。

抛开这些细节,重要的一点是,在 WebApplicationContext中配置了这些特别的 bean后,默认列表中的相同的 bean则被覆盖掉。

DispatcherSevlet 求的

建立了 DispatcherServlet后,一个请求到达其中之后, DispatcherServlet开始按照下面的流程处理请求。

1.  WebApplicationContext被搜到,作为一个属性绑定到这个请求之中,以便进这个流程中的控制器和其他元素能使用它。这个属性的键是 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE

2.  local resolver被绑定到请求中,以便流程中的元素能解析本地化,渲染视图,准备数据等等,不使用 local resolver的时候,可以忽略掉。

3.  theme resolver被绑定到请求之中,以便让视图检测何种主题被使用,如果不使用主题,可以忽略掉它。

4.  如果指定了 multipart文件解析器,该解析器会请求中检查是否是 mulitpart/form-data类型,是的话,把这个请求包装成一个 MultipartHttpServletRequest对象,以供 MultipartHandler去处理。

5.  经过这些准备阶段之后, DispatcherServlet会去寻找一个合适的处理器,如果找到,执行链会被赋予到这个处理器上(如拦截器和控制器),然后被执行去准备一个 Model对象或者渲染。

6.  如果 Model被返回,视图则被渲染;若未返回,则没有视图被渲染(因拦截器或者安全原因),因为这个请求可能已经完成了其责任。

 

Spring MVC 的入口

好了,上面这些所说基本上是 Spring MVC官方文档的内容,这里有一个重点,就是讲 DispatcherServlet那小节中所说的“它完全的集成了 Spring IoC容器,所以能让你使用 Spring框架所有的其他功能”。这个功能很关键,也就是说,在启动 Spring MVC框架本身的时候,需要启动 Spring IoC容器,这个是必须的,要不然,你在开发过程所写的那些 service dao,怎么被 Web层的 Controller调用呢?

首先看一下 Spring MVC配置的具体示例


 

<listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
      <servlet-name>SpringDispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:/root.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
</servlet>

 

Servlet规范中,规定了 Servlet容器在启动 Web应用启动的过程中,会处理一些开发者定义的监听器,而对于一个 Web应用中定义的 ServletContextListener监听器,是优先于 servlet filter,向 ServletContext中添加的,一旦 ServletContext被容器初始化,监听器的 contextInitialized()方法会被触发。 Spring MVC正是利用这个特性,编写了 Spring MVC专用的 ServletContextListener监听器,以保证 WebApplicationContext先被建立。

第二个要看的是 SpringDispathcer这个 servlet,它的 load-on-startup参数是 1,这个参数告诉 Web容器,保证这个 servlet class ServletContext启动后,要被加载到当前的 Web应用中并实例化,而且要执行这个 servlet中的 init()去初始化它。

好了,现在可以看出来, Spring MVC框架在 Web应用中的启动,是分为两部的,第一步,用一个 ServletContextListener来保证 WebApplicationContext先被建立,第二步,利用 DispatcherServlet来初始化一部分 Spring MVC前端使用的特定 bean,并且,这些 bean也需要放到 WebApplicationContext中被管理。

顺着这个思路,不妨做一些假设和提问

1.  Spring IoC容器中的内置 bean是在第一步被初始化的

2.  对于配置文件已配置的 bean,应该在哪一步被初始化

3.  对于被 @Service, @Compoent, @Dao, @Repository这些注解标注过的类,它们的 bean什么时候被初始化

4.  对于被 @Controller注解的类,因为它们是依赖 service component等中间层实现特定的功能,因此,它们至少因该在第三条中的所有 bean被初始化后,再行初始化。

5.  对于 @RequestMapping注解所制定的 URI匹配规则,应该在 Spring MVC启动的过程中,从 Controller类中扫瞄出来后,被统一管理。

这些假设或者提问不一定是正确的,但是先假设这样,有助于找出正确的答案。

 

<未完>

猜你喜欢

转载自mojarra.iteye.com/blog/1733585
MVC