《Spring3.X企业应用开发实战》学习笔记--SpringMVC


本篇是《Spring3.X企业应用开发实战》,陈雄华 林开雄著,电子工业出版社,2012.2出版”的学习笔记的第三篇,关于SpringMVC。

Spring MVC 3.0和早期版本相比拥有了一个质的飞跃,全面支持REST风格的WEB编程、完全注解驱动、处理方法签名非常灵活、处理方法不依赖于Servlet API等。

由于Spring MVC框架在后头做了非常多的隐性工作,所以想深入掌握Spring MVC 3.0并非易事,本章我们在学习Spring MVC的各项功能时,还深入其内部了解其后台的运作机理,只有了解这些机理后,才能更好地使用这个当前最先进的MVC框架。

服务器启动时加载配置文件的顺序:web.xml  applicationContext.xml  springmvc-servlet.xml。


Spring MVC概述


Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,无须实现任何接口,同时,Spring MVC还支持REST风格的URL请求:注解驱动及REST风格的Spring MVC是Spring 3.0最出彩的功能之一。

此外,Spring MVC在数据绑定、视图解析、本地化处理及静态资源处理上都有不俗的表现。


DispatcherServlet


Spring MVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet是Spring MVC的总导演、总策划,它负责截获请求并将其分派给相应的处理器处理。

在web.xml中配置DispatcherServlet,截获特定的URL请求:

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

Spring如何将上下文中的SpringMVC组件装配到DispatcherServlet中?


WebApplicationContext初始化后,此时Spring上下文中的Bean已经初始化完毕,开始执行DispatcherServlet的initStrategies()方法,代码如下:

protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(); //1.初始化上传文件解析器
 initLocalResolver();//2.初始化本地化解析器
 initThemeResolver();//3.初始化主题解析器
 initHandlerMappings();//4.初始化处理器映射器
 initHandlerAdapters();//5.初始化处理器适配器
 initHandlerExceptionResolver();//6.初始化处理器异常解析器
 initRequestToViewNameTranslator();//7.初始化请求道试图名解析器
 initViewResolvers();//8.初始化试图解析器
}
该方法的工作是通过反射机制查找并装配Spring容器中显式自定义的组件Bean,如果找不到,则装配默认的组件实例,默认的组件实例在spring-webmvc-版本.RELEASE.jar包中,org/springframework/web/servlet路径下的DispatcherServlet.properties中间中定义。

其中文件上传没有默认的解析器,如果需要,自行配置,比如:

<!-- 配置对文件上传的支持 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />


注解驱动的控制器


在POJO类定义处标注@Controller,再通过<context:component-scan/>扫描相应的类包,即可使POJO成为一个能处理HTTP请求的控制器。

@RequestMapping:不但支持标注的URL,还支持Ant风格(即?、*和**的字符)和带有{xxx}占位符的URL。带占位符的URL是Spring 3.0新增的功能,该功能在Spring MVC向REST目标挺进的发展过程中具有里程碑的意义。

通过@PathVariable可以将URL中的占位符参数绑定到控制器处理方法的入参中。


如何设置方法入参以绑定请求信息


使用命令/表单对象绑定请求参数值:

这是最常用的,即入参就是一个POJO。Spring MVC会按照请求参数名和对象属性名进行匹配,自动为对象填充属性值,支持级联的属性名,例如:

@RequestMapping("/handleInsert)

public String handleInsert(User user,String operator)…


使用@RequestParam、@CookieValue、@RequestHeader,分别获取请求、Cookie、请求报文头的传入值,他们都有3个参数:

value:参数名;

required:是否必需,默认为true,表示请求中必须包含对应的参数名,如果不存在将抛出异常;

defaultValue:默认参数名,设置该参数时,自动将required设为false。极少情况需要使用该参数,也不推荐使用该参数


使用Servlet API对象作为入参:

public String handleInsert(HttpServletRequest request,HttpServletResponse response)...

另外Spring MVC在 org.springframework.web.context.request包中定义了若干个可代理Servlet原生API类的接口,如WebRequest和NativeWebRequest,他们也允许作为代理类的入参,通过这些代理类可访问请求对象的任何信息。


使用IO对象作为入参:

Servlet的ServletRequest拥有getInputStream()和getReader()方法,可以通过他们读取请求的信息。相应Servlet的ServletResponse拥有getOutputStream()和getWriter()方法,可以通过它们输出响应信息。

Spring MVC允许控制器的处理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream/java.io.Writer作为方法的入参,Spring MVC将获取ServletRequest的InputStream/Reader或ServletResponse的OutputStream/Writer,然后传递给控制器的处理方法。


使用其他类型的参数:

比如java.util.Locale、java.security.Principal,可以通过Servlet的HttpServletRequest的getLocale()及getUserPrincipal()得到相应的值。


HttpMessageConverter<T>进行消息对象转换


HttpMessageConverter<T>是Spring 3.0新添加的一个重要接口,它负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息。

DispatcherServlet默认已经安装了AnnotationMethodHandlerAdapter作为HandlerAdapter的组件实现类,HttpMessageConverter即由AnnotationMethodHandlerAdapter使用,

将请求信息转换为对象,或将对象转换为响应信息。

Spring为HttpMessageConverter<T>提供了众多的实现类,他们组成了一个功能强大、用途广泛的HttpMessageConverter<T>家族。

AnnotationMethodHandlerAdapter默认一级装配了如下的HttpMessageConverter:

StringHttpMessageConverter

ByteArrayHttpMessageConverter

SourceHttpMessageConverter

XmlAwareFormHttpMessageConverter

如果需要装配其他类型的HttpMessageConverter,可在Spring的Web容器上下文中自行定义一个AnnotationMethodHandlerAdapter,如果在Spring容器中显式定义了一个
AnnotationMethodHandlerAdapter,则Spring MVC将使用它覆盖默认的AnnotationMethodHandlerAdapter。


如何使用HttpMessageConverter<T>将请求信息转换并绑定到处理方法的入参中呢?
Spring MVC提供了两种途径:
1.使用@RequestBody/@ResponseBody对处理方法进行标注
2.使用HttpEntity<T>/ResponseEntity<T>作为处理方法的入参或返回值


关于HttpMessageConverter<T>,得出如下几条结论:

1.当控制器处理方法使用到@RequestBody/@ResponseBody 或 HttpEntity<T>/ResponseEntity<T>时,Spring MVC才使用注册的HttpMessageConverter对请求/响应消息进行处理

2.当控制器处理方法使用到@RequestBody/@ResponseBody 或 HttpEntity<T>/ResponseEntity<T>时,Spring首先根据请求头或响应头的Accept属性选择匹配的HttpMessageConverter,进而根据参数类型或反向类型的过滤得到匹配的HttpMessageConverter,如果找不到可用的HttpMessageConverter将报错

3.@RequestBody/@ResponseBody不需要成对出现,如果方法入参使用到@RequestBody,Spring MVC选择匹配的HttpMessageConverter将请求消息转换并绑定到该入参中。如果处理方法标注了

4.@ResponseBody,Spring MVC选择匹配的HttpMessageConverter将方法返回值转换并输出相应消息。

5.HttpEntity<T>/ResponseEntity<T>的功用和@RequestBody/@ResponseBody相似


RestTemplate


RestTemplate是Spring 3.0新增的模板类,在客户端程序中可使用该类调用Web服务端的服务,它支持REST风格的URL。此外,它项AnnotationMethodHandlerAdapter一样拥有一个httpMessageConverter的注册表,它默认注册了5个HttpMessageConverter。


spring-servlet.xml


spring-servlet.xml配置(WEB-INFO目录下): 

<!-- 这是简单配置,代替bean节点那种显示加载bean的配置方式,可以自动加载必须得如下两个bean -->
<!-- <mvc:annotation-driven /> -->
<!-- 这是标准配置,可以解决ResponseBody中文乱码问题 -->
<bean  class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean
 class="org.springframework.http.converter.StringHttpMessageConverter">
 <property name="supportedMediaTypes">
  <list>
   <value>text/plain;charset=UTF-8</value>
  </list>
 </property>
 <property name="writeAcceptCharset" value="false"/>
</bean>
</list>
</property>
</bean> 
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /> 
<!-- 装载拦截器  -->
<mvc:interceptors>
<!-- 更改语言环境时,一个'locale'的请求参数发送  -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />  
<!-- 权限拦截  -->
<mvc:interceptor>
<mvc:mapping path="/**/*.do" />
<bean class="com.shopyp.authority.interceptor.AuthorityInterceptor"/>
</mvc:interceptor>  
</mvc:interceptors>


处理模型数据


对于MVC框架来说模型数据是最重要的,因为控制(C)是为了产生模型数据(M),而视图(V)则是为了渲染模型数据。

如何将模型数据暴漏给视图是Spring MVC框架的一项重要工作,Spring MVC提供了多种途径输出模型数据,介绍如下:

1、ModelAndView:处理方法返回值为ModelAndView时,方法体即可通过该对象添加模型数据

2、@ModelAttribute:方法入参标注该注解后,入参的对象就会放到数据模型中

3、Map及Model:入参为org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map时,处理方法返回时,Map中的数据会自动添加到模型中

4、@SessionAttribute:将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性


处理方法的数据绑定


我们知道Spring 会根据请求方法签名的不同,将请求信息中的信息以一定的方式转换并绑定到请求方法的入参中。

在请求消息到达真正调用处理方法的这一段时间内,Spring还完成了很多工作,包括数据转换、数据格式化及数据校验等。这一节使用较少,临时不去研究。


视图和视图解析器


请求处理方法执行完成后,最终返回一个ModelAndView对象。

对于那些返回String、View或ModelMap等类型的处理方法,Spring MVC也会在内部将它们装配成一个ModelAndView对象,它包含了视图逻辑名和模型对象的信息。

Spring MVC借助视图解析器(ViewResolver)得到最终的视图对象(View),这可能是我们常见的JSP视图,也可能是一个基于FreeMarker、Velocity模板技术的视图,还可能是PDF、Excel、XML、JSON等各种形式的视图。

对于最终究竟采取何种视图对象对模型对象进行渲染,Controller并不关心,Controller的工作重点聚集在生产模型数据的工作上,从而实现MVC的充分解耦。

FreeMarker配置:

 <!-- Freemarker配置,参考: http://www.cnblogs.com/hoojo/archive/2011/04/19/2020551.html-->
 <bean id="freemarkerConfig"
  class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
  <!-- 视图资源位置 -->
  <property name="templateLoaderPath" value="/WEB-INF/ftl/" />
  <property name="defaultEncoding" value="UTF-8" />
  <property name="freemarkerSettings">
   <props>
    <prop key="template_update_delay">0</prop><!-- 模板更新延时 -->
    <prop key="locale">zh_CN</prop>
    <prop key="default_encoding">UTF-8</prop>
    <prop key="output_encoding">UTF-8</prop>
    <prop key="template_exception_handler">rethrow</prop>
          <prop key="number_format">#.##</prop>
          <prop key="date_format">yyyy-MM-dd</prop>
          <prop key="time_format">HH:mm:ss</prop>
          <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
   </props>
  </property>
  <!-- 全局变量部分 -->
  <property name="freemarkerVariables">
   <map>
    <entry key="BasePath" value="${web.basepath}" />
    <entry key="xml_escape" value-ref="fmXmlEscape" />
   </map>
  </property>
 </bean>
 <bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape" />
 <!-- 配置freeMarker视图解析器 -->
 <bean id="ftlviewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" />
  <!-- 如果配置了这个节点,则视图必须是ftl,redirect等前缀都失效了 -->
  <!-- <property name="viewNames" value="*.ftl"/> -->
  <property name="contentType" value="text/html;charset=UTF-8" />
  <property name="cache" value="true" />
  <property name="prefix" value="" />
  <property name="suffix" value="" />
 </bean>

针对在实际开发过程中使用到的FreeMarker语法,JSTL标签等,可以逐渐熟悉和记录。

FreeMarker和Velocity是除JSP之外被使用最多的页面模板技术。页面模板编写好页面结构,模板页面中使用一些特殊的变量标识符绑定Java对象的动态数据。

FreeMarker是一个模板引擎,一个基于模板生产文本输出的通用工具,FreeMarker可以基于模板产生HTML、XML、JAVA源代码等多种类型的输出内容。虽然FreeMarker具有一些编程能力,单通常由Java程序准备数据,FreeMarker仅负责基于模板对模型数据进行渲染的工作。


关于Web开发


要想成为一名Web开发高手,不能仅满足于知道如何做,更要抛开线程探究本质。

笔者认为要成为一名Web开发高手,必须熟练了解如下内容:

1、每次请求和响应的背后究竟发生了哪些步骤,客户端服务器是如何通过HTTP请求报文进行交互的;

2、深刻掌握MIME类型的知识;

3、深刻掌握HTTP响应状态码的知识,如404、303究竟代表什么。


《Spring3.X企业应用开发实战》学习笔记--SpringMVC

上一篇:C++11 FAQ中文版--转


下一篇:安卓手机传递文件到Windows系统电脑