第3章 Spring MVC的常用注解
本章要点
- @Controller注解
- @RequestMapping注解
- @RequestParam注解
- @PathVariable注解
- @RequestHeader注解
- @CookieValue注解
- @SessionAttributes注解
- @ModelAttribute注解
- 转换JSON数据
- 转换XML数据
Spring 从2.5版开始引入注解,用户可以使用@Controller、@RequestMapping、@RequestParam/@ModelAttribute等类似这样的注解。到目前为止,Spring的版本虽然发生了很大的变化,但注解的特性却一直延续下来,并不断扩展,让广大开发者的工作变得更轻松。这都离不开Annotation的强大作用,本章将重点讲解Spring MVC 4中常用的注解。
3.1 @Controller注解
org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器,使用@Controller注解的类不需要继续特定的父类或者实现特定的接口,相对之前的版本实现Controller接口变得更加简单。而且Controller接口的实现类智能处理一个单一请求动作,而@Controller注解的控制器可以支持同时处理多个请求动作,更加灵活。
@Controller用于标记一个类,使用它标记的类就是一个SpringMVC Controller对象,即一个控制器类。Spring使用扫描机制查找应用程序中所有基于注解的控制器类。分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解,而使用@RequestMapping注解的方法才是真正处理请求的处理器。为了保证Spring能找到控制器,需要完成两件事情:
- 在Spring MVC 的配置文件的头文件中引入spring-context。
-
使用
<context:component-scan/>
元素,该元素的功能为:启动包扫描功能,以便注册带有@Controller、@Service、@repository、@Component等注解的类成为Spring 的Bean。base-package属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。
配置文件如下所示
<!--spring可以自动去扫描base-pack下面的包或者子包下面的Java文件,如果扫描到有Spring的相关注解的类,则把这些内注册为Spring的bean-->
<context:component-scan base-package="org.fkit.controller"/>
应该讲所有控制器类都放在基本包下,并且指定扫描该包,即org.fkit.controller,而不应该指定扫描org.fkit包,以免Spring MVC扫描了无关的包。
示例:@Controller注解的使用
新建一个项目ControllerTest,加入所需的.jar文件,示例代码如下:
package org.fkit.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model){
model.addAttribute("message","Hello World!");
return "helloWorld";
}
}
HelloWorldController是一个基于@Controller注解的控制器,@RequestMapping注释用来映射一个请求,value=“/helloWorld”表示请求由helloWorld方法进行处理。helloWorld方法接收一个org.springframework.ui.Model类型的参数,本例在model中添加了一个名为“message”的字符串对象,该对象可以在返回的视图当中通过request对象获取。最后,方法返回一个字符串“helloWorld”作为视图名称。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring可以自动去扫描base-pack下面的包或者子包下面的Java文件,如果扫描到有Spring的相关注解的类,则把这些内注册为Spring的bean-->
<context:component-scan base-package="org.fkit.controller"/>
<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix">
<value>/WEB-INF/content</value>
</property>
<!--后缀-->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
由于使用了注解类型,因此不需要再在配置文件中使用XML描述Bean。<context:component-sacn base-package="org.fkit.controller"/>
指定需要Spring 扫描org.fkit.controller包及其子包下面的所有Java文件。最后配置了视图解析器InternalResourceViewResolver来解析视图,将View呈现给用户。视图解析器中配置的prefix属性表示视图的前缀,suffix表示视图的后缀,返回的视图字符串是”helloWorld”,经过视图解析器之后,则视图的完整路径为:/WEB-INF/content/helloWorld.jsp。需要注意的是,此处没有配置处理映射器和处理器适配器,当用户没有配置这两项时,Spring 会使用默认的处理映射器和处理器适配器处理请求。
此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本一致,故此处不再赘述,读者可自行配置。
部署ControllerTest这个Web应用,在浏览器中输入如下URL来测试应用:
http://localhost:8080/helloWorld
会看到如图3.1所示的界面,表示Spring MVC访问成功。
Spring MVC中用于参数绑定的注解很多,都在org.springframework.web.bind.annotation包中,根据它们处理的request的不同内容部分可以分为四类(主要讲解常用类型)。
- 处理request body部分的注解: @RequestParam、@RequestBody。
- 处理request uri部分的注解: @PathVariable。
- 处理request header部分的注解: @RequestHeader、@CookieValue。
- 处理attribute类型的注解: @SessionAttributes、@ModelAttribute。
3.2 @RequestMapping注解
3.2.1 @RequestMapping注解
开发者需要在控制器内部为每一个请求动作开发相应的处理方法。org.springframework.web.bind.annotation.RequestMapping注解类型知识Spring用哪一个类或方法来处理请求动作,该注解可用于类或方法。
提示:
@RequestMapping虽然也在org.springframework.web.bind.annotation下面,但是严格来说,它并不属于参数绑定注解。
@RequestMapping可以用来注释一个控制器类,在这种情况下,所有方法都将映射为相对于类级别的请求,表示该控制器处理的所有请求都被映射到value属性所指示的路径下。示例代码如下:
package org.fkit.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value = "/register")
public String register() {
return "register";
}
@RequestMapping(value = "/login")
public String login() {
return "login";
}
}
由于UserController类加了value=“/user”的@RequestMapping注解,因此所有相关路径都要加上“/user”,此时方法被映射到了如下请求URL:
http://localhost:8080/user/register
http://localhost:8080/user/login
使用@RequestMapping注解可以指定如表3.1所示的属性
属性 | 类型 | 是否必要 | 说明 |
---|---|---|---|
value | String[] | 否 | 用于将指定请求的实际地址映射到方法上 |
name | String | 否 | 给映射地址指定一个别名 |
method | RequestMethod[] | 否 | 映射指定请求的方法类型,包括GET、POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE |
consumes | String[] | 否 | 指定处理请求的提交内容类型(Content-Type),例如application/json、text/html等 |
produces | String[] | 否 | 指定返回的内容类型,返回的内容类型必须是request请求头(Accept)中所包含的类型 |
params | String[] | 否 | 指定request中必须包含某些参数值时,才让该方法处理 |
headers | String[] | 否 | 指定request中必须包含某些指定的header值,才能让该方法处理请求 |
Path | String[] | 否 | 在Servlet环境中只有:uri路径映射(例如”/myPath.do”)。也支持如ant的基于路径模式(例如“/myPath/*“)。在方法层面上,支持相对路径(例如”edit.do“) |
@RequestMapping注解支持的常用属性示例如下。
@RequestMapping注解支持的常用属性示例如下。
1. value属性
@RequestMapping用来映射一个请求和一种方法。可以使用@RequestMapping注释一个方法或类。
一个采用@RequestMapping注释的方法将成为一个请求处理方法,例如:
@RequestMapping(value="/hello")
public ModelAndView hello(){
return ...;
}
该示例使用@RequestMapping注释的value属性将URL映射到方法上。在这个例子中,将hello映射到hello方法上,使用如下URL访问应用时将由hello方法进行处理。
http://localhost:8080/context/hello
由于value属性是@RequestMapping注释的默认属性,因此,如果只有唯一的属性,则可以省略属性名,即如下两个标注含义相同。
@RequestMapping(value="/hello")
@RequestMapping("/hello")
但如果有超过一个属性,就必须写上value属性名称。
value属性的值也可以是一个空字符串,此时该方法被映射到如下请求URL:
http://localhost:8080/context
2. method属性
该属性用来指示该方法仅仅处理哪些HTTP请求方式。
@RequestMapping(value="/hello",method=RequestMethod.POST)
以上代码method=RequestMethod.POST表示该方法只支持POST请求。
也可以同时支持多个HTTP请求方式。如:
@RequestMapping(value="/hello",method=[RequestMethod.POST,RequestMethod.GET])
如果没有指定method属性值,则请求处理方法可以处理任意HTTP请求方式。
3. consumes属性
该属性指定处理请求的提交内容类型(Content-Type)
@RequestMapping(value="/hello",method=RequestMethod.POST,consumes="application/json")
表示方法仅处理request Content-Type为“application/json”类型的请求。
4. produces属性
该属性指定返回的内容类型,返回的内容类型必须是request请求头(Accept)中所包含的类型。
@RequestMapping(value="/hello",method=RequestMethod.POST,produces="application/json")
方法仅处理request请求中Accept头中包含了“application/json”的请求,同时指明了返回的内容类型为application/json。
5. params属性
该属性指定request中必须包含某些参数值时,才让该方法处理。
@RequestMapping(value="/hello",method=RequestMethod.POST,params="myParam=myValue")
方法仅处理其中名为“myParam”、值为”myValue“的请求。
6. headers属性
该属性指定request中必须包含某些指定的header值,才能让该方法处理请求。
@RequestMapping(value="/hello",method=RequestMethod.POST,headers="Referer=http://www.fkit.org/")
方法仅处理request的header中包含了“Referer”请求头和对应值为“http://www.fkit.org/“的请求。
3.2.2 请求处理方法可出现的参数类型
每个请求处理方法可以有多个不同类型的参数。
如果需要访问HttpServletRequest对象,则可以添加HttpServletRequest作为参数,Spring会将对象正确地传递给方法:
@RequestMapping(value = "/login")
public String login(HttpServletRequest request) {
return "login";
}
如果需要访问HttpSession对象,则可以添加HttpSession作为参数,Spring会将对象正确地传递给方法:
@RequestMapping(value = "/login")
public String login(HttpSession session) {
return "login";
}
下面是可以在请求处理方法中出现的参数类型:
- javax.servlet.ServletRequest或javax.servlet.http.HttpServletRequest
- javax.servlet.ServletResponse或javax.servlet.http.HttpServletResponse
- javax.servlet.http.HttpSession
- org.springframework.web.context.request.WebRequest或org.springframework.web.context.request.NativeWebREquest
- java.util.Locale
- java.io.InputStream或Java.io.Reader
- java.io.OutputStream或Java.io.Writer
- java.security.Principal
- HttpEntity<?>
- java.util.Map
- org.springframework.ui.Model
- org.springframework.ui.ModelMap
- org.springframework.web.servlet.mvc.support.RedirectAttributes
- org.springframework.validation.Errors
- org.springframework.validation.BindingResult
- org.springframework.web.bind.support.SessionStatus
- org.springframework.web.util.UriComponentsBuilder
- @PathVariable、@@MatrixVariable注解
- @RequestParam、@RequestHeader、@RequestBody、@RequestPart注解
其中最终要的是org.springframework.ui.Model类型。这不是一个ServletAPI类型,而是一个Spring MVC类型,其中包含了Map对象用来存储数据。如果方法中添加了Model参数,则每次调用请求处理方法时,Spring MVC都会创建Model对象,并将其作为参数传递给方法。
3.2.3 请求处理方法可返回的类型
每个请求处理方法可以返回一下类型的返回结果。
- org.springframework.web.portlet.ModelAndView
- org.springframework.ui.Model
- java.util.Map<k,v>
- org.springframework.web.servlet.View
- java.lang.String
- HttpEntity或ResponseEntity
- java.util.concurrent.Callable
- org.springframework.web.context.request.async.DeferredREsult
- void
3.2.4 Model和ModelAndView
在请求处理方法可出现和返回的参数类型中,最重要的就是Model和ModelAndView了。对于MVC框架,控制器(Controller)执行业务逻辑,用于产生模型数据(Model),而视图(View)则用于渲染模型数据。
如何将模型数据传递给视图是Spring MVC框架的一项重要工作,Spring MVC提供了多种途径输出模型数据,如:
- Model和ModelMap
- ModelAndView
- @ModelAttribute
- @SessionAttributes
下面将重点介绍Model、ModelMap以及ModelAndView,@ModelAttribute和@SessionAttributes将在3.3节中重点介绍。
1. Model和ModelMap
Spring MVC在内部使用了一个org.springframework.ui.Model接口存储模型数据,它的功能类似java.util.Map接口,但是比Map易于使用。org.springframework.ui.ModelMap接口实现了Map接口。
Spring MVC在调用处理方法之前会创建一个隐含的模型对象,作为模型数据的存储容器。如果处理方法的参数为Model或ModelMap类型,则Spring MVC会将隐含模型的引用传递给这些参数。在处理方法内部,开发者就可以通过这个参数对象访问模型中的所有数据,也可以向模型中添加新的属性数据。
在处理方法中,Model和ModelMap对象都可以使用如下方法添加模型数据:
- addObject(String attributeName,Object attributeValue);
示例:Model和ModelMap的使用
package org.fkit.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class User1Controller {
private static final Log logger = LogFactory.getLog(UserController.class);
//@ModelAttribute修饰的方法会先于login调用,该方法用于接收前台jsp页面传入的参数
@ModelAttribute
public void userModel(String loginname, String password, Model model) {
logger.info("userModel");
//创建User对象存储jsp页面传入的参数
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
//将User对象添加到Model当中
model.addAttribute("user", user);
}
@RequestMapping(value = "/login1")
public String login(Model model) {
logger.info("login");
//从Model当中取出之前传入的名为user的对象
User user = (User) model.asMap().get("user");
System.out.println(user);
//设置user对象的username属性
user.setUsername("测试");
return "result1";
}
}
@ModelAttribute修饰的userModel方法会先于login调用,它把请求参数赋给对应变量,可以向方法中的Model添加对象,前提是要在方法签名中加入一个Model类型的参数。
package org.fkit.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fkit.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class User2Controller {
private static final Log logger = LogFactory.getLog(User2Controller.class);
//@ModelAttribute修饰的方法会先于login调用,该方法用于接收前台jsp页面传入的参数
@ModelAttribute
public void userModel(String loginname, String password, ModelMap modelMap) {
logger.info("userMode2");
//创建User对象存储jsp页面传入的参数
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
//将User对象添加到Model当中
modelMap.addAttribute("user", user);
}
@RequestMapping(value = "/login2")
public String login(ModelMap modelMap) {
logger.info("login2");
//从Model当中取出之前传入的名为user的对象
User user = (User) modelMap.get("user");
System.out.println(user);
//设置user对象的username属性
user.setUsername("测试");
return "result2";
}
}
User2Controller和User1Controller的代码功能基本一致,只是存储对象由Model改成了ModelMap。
2. ModelAndView
控制器处理方法的返回值如果是ModelAndView,则其既包含模型数据信息,也包含视图信息,这样Spring MVC将使用包含的视图对模型数据进行渲染。可以简单地将模型数据看成一个Map<String,Object>
对象。
在处理方法中可以使用ModelAndView对象的如下方法添加模型数据:
addObject(String attributeName,Object attributeValue);
可以通过如下方法设置视图:
setViewName(String viewName)
示例:ModelAndView的使用
package org.fkit.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fkit.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class User3Controller {
private static final Log logger = LogFactory.getLog(User3Controller.class);
@ModelAttribute
public void userModel(String loginname, String password, ModelAndView mv) {
logger.info("userMode3");
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
mv.addObject("user", user);
}
@RequestMapping(value = "/login3")
public ModelAndView login(ModelAndView mv) {
logger.info("login3");
//从ModelAndView的Model当中取出之前存入的名为user的对象
User user = (User) mv.getModel().get("user");
System.out.println(user);
//设置user对象的username属性
user.setUsername("测试");
//设置返回的视图名称
mv.setViewName("result3");
return mv;
}
}
User3Controller和User1Controller的代码功能基本一致,只是存储对象改成了ModelAndView。
3.3 参数绑定注解
3.3.1 @RequestParam注解
org.springframework.web.bind.annotation.RequestParam注解类型用于将指定的请求参数赋值给方法中的形参。
使用@RequestParam注解可指定如表3.2所示的属性。
属性 | 类型 | 是否必要 | 说明 |
---|---|---|---|
name | String | 否 | 指定请求头绑定的名称 |
value | String | 否 | name属性的名 |
required | boolean | 否 | 指示参数是否必须绑定 |
defaultValue | String | 否 | 如果没有传递参数而使用的默认值 |
请求处理方法参数的可选类型为Java基本数据类型和String。示例代码如下:
package org.fkit.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class UserController {
@RequestMapping(value = "/login")
public ModelAndView login(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password) {
return ……;
}
}
假设请求如下:
http://localhost:8080/context/login?loginname=jack&password=123456
以上代码会将请求中的loginname参数的值“jack”赋给loginname变量,password参数的值“123456”赋给password变量。
@RequestParam还有如下写法:
@RequestParam(value="loginname",required=true,defaultValue="admin")
其中required参数不是必须的,默认为true。
示例:@RequestMapping和@RequestParam注解的使用
新建一个项目RequestMappingTest,加入需要的jar文件,示例代码如下:
package org.fkit.entity;
import java.io.Serializable;
public class User implements Serializable {
//私有字段
private String loginname;
private String password;
private String username;
//公共构造器
public User() {
super();
}
//set/get方法
public String getLoginname() {
return loginname;
}
public void setLoginname(String loginname) {
this.loginname = loginname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
User是一个域对象,用来接收并封装前台页面传递过来的数据。
package org.fkit.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fkit.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.ArrayList;
import java.util.List;
// Controller注解用于指示该类是一个控制器,可以同时处理多个请求
@Controller
// RequestMapping可以用来注释一个控制类,此时,所有方法都将映射为相对于类级别的请求
//表示该控制器处理的所有请求都被映射到value属性所指示的路径下
@RequestMapping(value = "/user")
public class UserController {
// 静态List<User>集合,此处代替数据库用来保存注册的用户信息
private static List<User> userList;
// UserController类的构造器,初始化List<User>集合
public UserController() {
super();
userList = new ArrayList<User>();
}
// 静态的日志类LogFactory
private static final Log logger = LogFactory.getLog(UserController.class);
// 该方法映射的请求为http://localhost:8080/RequestMappingTest/user/register,该
// 方法支持GET请求
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String registerForm() {
logger.info("register GET 方法被调用……");
// 跳转到注册页面
return "registerForm";
}
// 该方法映射的请求为http://localhost:8080/RequestMappingTest/user/register,该
// 方法支持POST请求
@RequestMapping(value = "/register", method = RequestMethod.POST)
// 将请求中的loginname参数的值赋给loginname变量,password和username同样处理
public String register(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
@RequestParam("username") String username) {
logger.info("register POST 方法被调用……");
// 创建User对象
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
user.setUsername(username);
// 模拟数据库存储User信息
userList.add(user);
// 跳转到登录页面
return "loginForm";
}
// 该方法映射的请求为http://localhost:8080/RequestMappingTest/user/login
@RequestMapping("/login")
public String login(
//将请求中的loginname参数的值赋给loginname变量,password同样处理
@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
Model model) {
logger.info("登录名:" + loginname + " 密码:" + password);
//到集合中查找用户是否存在,此处用来模拟数据库验证
for (User user : userList) {
if (user.getLoginname().equals(loginname) && user.getPassword().equals(password)) {
model.addAttribute("user", user);
return "welcome";
}
}
return "loginForm";
}
}
UserController类的代码解释如下:
- UserController类使用了@Controller注解,是一个控制类。
- UserController类上面使用了@RequestMapping(value=“/user”)注解,表示该控制器处理的所有请求都被映射到user路径下。
- 本来没有使用数据库存储用户注册信息,所以定义了一个静态的List集合userList用来代替数据库存储用户数据。
- registerForm方法使用了@RequestMapping(value=“/register”,method=RequestMethod.GET)注解,表示该方法映射的请求为http://localhost:8080/RequestMappingTest/user/register,并且只支持GET请求。该方法返回字符串“registerForm”,参考springmvc-config.xml中的配置信息,可以知道该方法只是跳转到registerForm.jsp注册页面。
- register方法使用了@RequestMapping(value=“/register”,method=RequestMethod.POST)注解,表示该方法映射的请求为http://localhost:8080/RequestMappingTest/user/register,并且只支持POST请求。该方法使用@RequestParam注解将指定的请求参数赋值给方法中形参,之后创建了一个User对象保存用户传递的注册信息,最后将User对象存储到userList集合当中,之后的登录页面就可以到userList集合当中进行用户登录业务逻辑的判断。该方法返回字符串“loginForm”,并跳转到loginForm.jsp登录页面。
- login方法使用了@RequestMapping(“/login”)注解,表示该方法映射的请求为http://localhost:8080/RequestMappingTest/user/login,这里没有设置method属性表示支持所有方式的请求。该方法也使用@RequestParam注解将指定的请求参数赋值给方法中的形参。之后到集合中查找用户是否存在,此处用来模拟数据库验证。login方法中还有一个参数Model对象,调用该对象的addAttribute方法可以将数据添加到request当中(关于Model对象的知识,在第4章重点介绍)。最后,如果用户登录成功则返回字符串“welcome”,并跳转到welcome.jsp欢迎页面;登录失败则返回字符串“loginForm”,并跳转到loginForm.jsp登录页面。
提示:
registerForm方法和register方法虽然映射的请求一样,但是registerForm方法支持的是GET请求,而register方法支持的是POST请求。
<h3>注册页面</h3>
<br>
<form action="/user/register" method="post">
<table>
<tr>
<td><label for="loginname">登录名:</label></td>
<td><input type="text" id="loginname" name="loginname"></td>
</tr>
<tr>
<td><label for="password">密码:</label></td>
<td><input type="password" id="password" name="password"></td>
</tr>
<tr>
<td><label for="username">真实姓名:</label></td>
<td><input type="text" id="username" name="username"></td>
</tr>
<tr>
<td><input type="submit" id="submit" value="注册"></td>
</tr>
</table>
</form>
registerForm.jsp是一个注册页面,用户可以输入登录名、密码和真实姓名,该表单被提交到register请求。注意,这里使用的是POST方式,响应请求的是UserController类的register方法。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录页面</title>
</head>
<body>
<h3>登录页面</h3>
<br>
<form action="/user/login" method="post">
<table>
<tr>
<td><label for="loginname">登录名:</label></td>
<td><input type="text" id="loginname" name="loginname"></td>
</tr>
<tr>
<td><label for="password">密码:</label></td>
<td><input type="password" id="password" name="password"></td>
</tr>
<tr>
<td><input type="submit" id="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
loginForm.jsp是一个登录页面,用户可以输入登录名和密码,该表单被提交到login请求。这里使用的是POST方式,响应请求的是UserController类的login方法。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎</title>
</head>
<body>
<h3>欢迎[${requestScope.user.username}]登录</h3>
</body>
</html>
welcome.jsp是一个欢迎页面,用户登录成功后跳转到该页面,该页面使用EL表达式访问request当中的user对象的username属性。
此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本一致,故此处不再赘述,读者可自行配置。
部署RequestMappingTest这个Web应用,在浏览器中输入如下URL来测试应用:
http://localhost:8080/user/register
提示:
可能出现的中文乱码情况,需要在web.xml文件中增加编码过滤器
<!-- 编码过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
可看到如图3.2所示的界面,表示Spring MVC成功跳转到注册界面registerForm.jsp。
输入登录名“test”,密码“123456”,真实姓名“测试用户”,单击“注册”按钮。请求将会被提交到UserController类的register方法进行注册,注册的用户信息会保存到UserController类的userList静态集合中。注册成功,将会跳转到如图3.3所示的登录界面。
输入登录名“test”,密码“123456”,单击“登录”按钮。请求将会被提交到UserController类的login方法进行登录验证,验证成功,将会跳转到如图3.4所示的欢迎界面。
3.3.2 @PathVariable注解
org.springframework.web.bind.annotation.PathVariable注解类型可以非常方便地获得请求URL中的动态参数。@PathVariable注解只支持一个属性value,类型为String,表示绑定的名称,如果省略则默认绑定同名参数。示例代码如下:
@RequestMapping(value="/pathVariableTest/{userId}")
public void pathVariableTest(@PatherVariable Integer userId){
……
}
加入请求的URL为“http://localhost:8080/pathVariableTest/1”,则自动将URL中模板变量{userId}绑定到通过@PathVariable注解的同名参数上,即userId变量将赋值为1。
3.3.3 @RequestHeader注解
org.springframework.web.bind.annotation.RequestHeader注解类型用于将请求头信息区数据映射到功能处理方法的参数上。
使用@RequestHeader注解可指定如表3.3所示的属性。
属性 | 类型 | 是否必要 | 说明 |
---|---|---|---|
name | String | 否 | 指定请求头绑定的名称 |
value | String | 否 | name属性的别名 |
required | boolean | 否 | 指示参数是否必须绑定 |
defaultValue | String | 否 | 如果没有传递参数而使用的默认值 |
@RequestHeader注解示例代码如下:
@RequestMapping(value="/requestHeaderTest")
public void requestHeaderTest(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value="Accept") String[]accepts)
以上配置自动将请求头“User-Agent”的值赋到userAgent变量上,并将“Accept”请求头的值赋到accepts参数上。
org.springframework.web.bind.annotation.CookieValue用于将请求的Cookie数据映射到功能处理方法的参数上。
使用@CookieValue注解可指定如表3.4所示的属性。
属性 | 类型 | 是否必要 | 说明 |
---|---|---|---|
name | String | 否 | 指定请求头绑定的名称 |
value | String | 否 | name属性的别名 |
required | boolean | 否 | 指示参数是否必须绑定 |
defaultValue | String | 否 | 如果没有传递参数而使用的默认值 |
@CookieValue注解示例代码如下:
@RequestMapping(value="/cookieValueTest")
public void cookieValueTest(
@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)
以上配置会自动将JSESSIONID值设置到sessionId参数上,defaultValue表示Cookie中没有JSESSIONID时默认为空。
示例:@PathVariable、@RequestHeader和@CookieValue注解的使用。
新建一个项目DataBindingTest,加入所需的jar文件,示例代码如下。
package org.fkit.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class DataBindingController {
// 静态的日志类LogFactory
private static final Log logger = LogFactory.getLog(DataBindingController.class);
// 测试@PathVariable注解
// 该方法映射的请求为http://localhost:8080/pathVariableTest/{userId}
@RequestMapping(value = "/pathVariableTest/{userId}")
public void pathVariableTest(@PathVariable Integer userId) {
logger.info("通过@PathVathVariable获得数据:" + userId);
}
// 测试@RequestHeader注解
// 该方法映射的请求为http://localhost:8080/requestHeaderTest
@RequestMapping(value = "/requestHeaderTest")
public void requestHeaderTest(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value = "Accept") String[] accepts) {
logger.info("通过@RequestHeaderTest获得数据:" + userAgent);
for (String accept :
accepts) {
logger.info(accept);
}
}
// 测试@CookieValue注解
@RequestMapping(value = "/cookieValueTest")
public void cookieValueTest(
@CookieValue(value = "JSESSIONID", defaultValue = "") String sessionId) {
logger.info("通过@RequestHeaderTest获得数据:" + sessionId);
}
}
DataBindingTest类的代码解析如下:
- pathVariableTest(@PathVariable Integer userId)方法用于测试@PathVariable注解,它会将请求路径“/pathVariableTest/{userId}“中userId的值设置到方法参数的userId变量当中。
- requestHeaderTest(@RequestHeader(“User-Agent”) String userAgent)和@RequestHeader(value=“Accept”) String[] accepts)方法用于测试@RequestHeader注解,它会将请求头“User-Agent”的值赋到userAgent变量上,并将“Accept”请求头的值赋到accepts参数上。
- cookieValueTest(@CookieValue(value=“JSESSIONID” ,defaultValue=“”) String sessionId)方法会自动将JSESSIONID值入参到sessionId参数上,defaultValue表示Cookie中没有JSESSION是默认为空。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>数据绑定测试</title>
</head>
<body>
<h2>数据绑定测试</h2>
<a href="/pathVariableTest/1">测试@PathVariable注解</a><br><br>
<a href="/requestHeaderTest">测试@RequestHeader注解</a><br><br>
<a href="/cookieValueTest">测试@CookieValue注解</a>
</body>
</html>
此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本一致,故此处不再赘述,读者可自行配置。
同时Spring MVC还需要springmvc-conofig.xml配置文件,该文件内容和ControllerTest项目中的springmvc-config.xml文件一致,读者可自行配置。
部署DataBindingTest这个Web应用,在浏览器中输入如下URL来测试应用:
http://localhost:8080
会看到如图3.5所示的界面,表示Spring MVC成功跳转到初始化页面index.jsp。
3.3.5 @SessionAttriabutes注解
org.springframework.web.bind.annotation.SessionAttributes注解类型允许我们有选择地指定Model中的哪些属性需要转存到HttpSession对象当中。
使用@SessionAttributes注解可指定如表3.5所示的属性。
属性 | 类型 | 是否必要 | 说明 |
---|---|---|---|
names | String[] | 否 | Model中属性的名称,即存储在HttpSession当中的属性名称 |
value | String[] | 否 | names属性的别名 |
types | Class<?>[] | 否 | 指示参数是否必须绑定 |
@SessionAttributes只能声明在类上,而不能声明在方法上。
示例:@SessionAttributes注解的使用
package org.fkit.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fkit.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
// 将Model中的属性名为user的属性放入HttpSession对象当中
@SessionAttributes("user")
public class SessionAttributesController {
// 静态的日志类LogFactory
private static final Log logger = LogFactory.getLog(SessionAttributes.class);
// 该方法映射的请求为http://localhost:8080/{formName}
@RequestMapping(value = "/{formName}")
public String loginForm(@PathVariable String formName) {
logger.info("loginForm 被调用……");
// 动态跳转页面
return "/"+formName;
}
//该方法映射的请求为http://localhost:8080/login
@RequestMapping(value = "/login")
public String login(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
Model model) {
logger.info("login 被调用……");
// 创建User对象,装载用户信息
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
user.setUsername("admin");
// 将user对象添加到Model当中
model.addAttribute("user", user);
return "/welcome";
}
}
接下来创建一个登录页面loginForm.jsp,该页面代码和前面RequestMappingTest的代码相同,读者可以参考配套资源中的代码。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎</title>
</head>
<body>
<h3>欢迎[${requestScope.user.username}]登录</h3>
<%--访问request作用域中的user对象;--%>
${requestScope.user.username} <br>
<%--访问session作用域中的user对象--%>
${sessionScope.user.username} <br>
</body>
</html>
在浏览器中输入如下URL来测试应用
http://localhost:8080/loginForm
会看到如图3.6所示界面
输入登录名“test”,密码“123456”,单击“登录”按钮。请求将会被提交到SessionAttributesController类的login方法,该方法将会创建User对象来保存数据,并将其设置到Model当中。因为类上面使用了@SessionAttributes注解,故User同时也会被设置到HttpSession作用域当中。方法执行完跳转到如图3.7所示的欢迎界面。
可以看到,User对象被成功设置到了HttpSession作用域当中。
@SessionAttributes还有如下写法:
@SessionAttributes(types={User.class},value="user")
还可以设置多个对象到HttpSession当中:
@SessionAttributes(types={User.class,Dept.class},value={"user","dept"})
types属性用来指定放入HttpSession当中的对象类型。
3.3.6 @ModelAttribute注解
org.springframework.web.bind.annotation.ModelAttribute注解类型将请求参数绑定到Model对象。
@ModelAttribute注解只支持一个属性value,类型为String,表示绑定的属性名称。
提示:
被@ModelAttribute注释的方法会在Controller每个方法执行前被执行,因此在一个Controller映射到多个URL时,要谨慎使用。
@ModelAttribute注解的使用方式有很多种,下面为读者逐一介绍。
示例:@ModelAttribute注解的使用
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试@ModelAttribute的不同用法</title>
</head>
<body>
<h3>测试@ModelAttribute的不同用法<</h3>
<a href="/loginForm1">测试@ModelAttribute(value="")注释返回具体类的方法</a>
<br><br>
<a href="/loginForm2">测试@ModelAttribute注释void返回值的方法</a><br><br>
<a href="/loginForm3">测试@ModelAttribute注释返回具体类的方法</a><br><br>
<a href="/loginForm4">测试@ModelAttribute和@RequestMapping同时注释一个方法</a>
<br><br>
<a href="/loginForm5">测试@ModelAttribute注释一个方法的参数</a><br><br>
</body>
</html>
package org.fkit.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class FormController {
// 该方法映射请求为http://localhost:8080/{formName}
@RequestMapping(value = "/{formName}")
public String loginForm(@PathVariable String formName) {
// 动态跳转页面
return "/" + formName;
}
}
1. 测试@ModelAttribute(value=“”)注释返回具体类的方法
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试@ModelAttribute(value=“”)注释返回具体类的方法</title>
</head>
<body>
<h3>测试@ModelAttribute(value=“”)注释返回具体类的方法</h3>
<form action="/login1">
<table>
<tr>
<td>
<label for="loginname">登录名:</label>
</td>
<td>
<input type="text" id="loginname" name="loginname"/>
</td>
</tr>
<tr>
<td>
<input type="submit" id="submit" value="登录">
</td>
</tr>
</table>
</form>
</body>
</html>
package org.fkit.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class ModelAttribute1Controller {
// 使用@ModelAttribute注释的value属性,来指定model属性的名称,model属性的值就是方法的
// 返回值
@ModelAttribute("loginname")
public String userModel1(
@RequestParam("loginname") String loginname){
return loginname;
}
@RequestMapping(value = "/login1")
public String login1(){
return "/result1";
}
}
ModelAttribute1Controller类中除了@RequestMapping映射的login1方法之外,还提供了一个userModel1方法,该方法上有一个@ModelAttribute注解,此处@ModelAttribute注解默认的value值为”loginname“,用来指定model属性的名称,而model属性的值就是userModel1方法的返回值。被@ModelAttribute注解的userModel1方法会先于login1调用,它把请求参数loginname的值赋给loginname变量,并设置了一个属性loginname到Model当中,而属性的值就是loginname变量的值。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
访问request作用范围域中的loginname对象:${requestScope.loginname}
<br>
</body>
</html>
在跳转的result1.jsp中可以访问到由@ModelAttribute设置的loginname的值。
此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet。因为每次配置基本一致,故此处不再赘述,读者可执行配置。
同时,Spring MVC还需要springmvc-config.xmo配置文件,该文件内容和ControllerTest项目中的springmvc-config.xml文件一致,读者可自行配置。
部署ModelAttributeTest这个Web应用,在浏览器中输入如下URL来测试应用:
http://localhost:8080/
会看到如图3.8所示的界面,表示Spring MVC成功跳转到初始页面index.jsp。
点击“测试@ModelAttribute(value=””)注释返回具体类的方法”超链接发送请求,跳转到loginForm1.jsp,如图3.9所示。
输入登录名“test”,单击“登录”按钮发送请求,而后将先调用哦userModel1方法,再调用login1方法,并跳转到result1.jsp,如图3.10所示。
可以看到,在request作用域中访问到了Model的值。
2. 测试@ModelAttribute注释void返回值的方法
<h3>测试@ModelAttribute注释void返回值的方法</h3>
<form action="/login2">
<table>
<tr>
<td>
<label for="loginname">登录名:</label>
</td>
<td>
<input type="text" id="loginname" name="loginname">
</td>
</tr>
<tr>
<td>
<label for="password">密码:</label>
</td>
<td>
<input type="password" id="password" name="password">
</td>
</tr>
<tr>
<td>
<input type="submit" id="submit" value="登录">
</td>
</tr>
</table>
</form>
@Controller
public class ModelAttribute2Controller {
//model属性名称和值由model.addAttribute()实现,前提是要在方法中加入一个
// Model类型的参数
@ModelAttribute
public void userModel2(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
Model model) {
model.addAttribute("loginname", loginname);
model.addAttribute("password", password);
}
@RequestMapping(value = "/login2")
public String login2() {
return "/result2";
}
}
ModelAttribute2Controller类中除了@RequestMapping映射的login2方法之外,还提供了一个userModel2方法,该方法上有一个@ModelAttribute注解。userModel2方法会先于login2调用,它把请求参数值赋给对应变量,model属性名称和值由model.addAttribute()方法实现,前提是要在方法中加入一个Model类型的参数。
<%--访问request作用域中的loginname对象--%>
${requestScope.loginname}<br>
<%--访问request作用域中的password对象--%>
${requestScope.password}<br>
在跳转的result2.jsp中可以访问到由@ModelAttribute设置的loginname和password的值。
在浏览器中输入如下URL来测试应用:http://localhost:8080/ ,将会跳转到如图3.8所示页面,点击“测试@ModelAttribute注释void返回值的方法”超链接发送请求,将会跳转到loginForm2.jsp页面,如图3.11所示。
输入登录名“test”,密码“123456”,单击“登录”按钮发送请求,而后将先调用userModel2方法,再调用login2方法,并跳转到result2.jsp页面,如图3.12所示。
可以看到,在request作用域中访问到了Model的值。
3. 测试@ModelAttribute注释返回具体类的方法
loginForm3.jsp和loginForm2.jsp页面内容一致,读者可自行参考,此处不再赘述。
@Controller
public class ModelAttribute3Controller {
// 静态List<User>集合,此处代替数据库用来保存注册的用户信息
private static List<User> userList;
// ModelAttribute3Controller类的构造器,初始化List<User>集合
public ModelAttribute3Controller() {
super();
userList = new ArrayList<User>();
User user1 = new User("test", "123456", "测试用户");
User user2 = new User("admin", "123456", "管理员");
// 存储User用户,用于模拟数据库数据
userList.add(user1);
userList.add(user2);
}
// 根据登录名和密码查询用户,用户存在返回包含用户信息的User对象,不存
// 在返回null
public User find(String loginname, String password) {
for (User user : userList) {
if (user.getLoginname().equals(loginname) && user.getPassword().equals(password)) {
return user;
}
}
return null;
}
// model属性的名称没有被定义,它由返回类型隐含表示,如这个方法返回User类
// 型,那么这个model属性的名称是user。
@ModelAttribute
public User userModel3(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password) {
return find(loginname, password);
}
@RequestMapping(value = "/login3")
public String login3() {
return "/result3";
}
}
ModelAttribute3Controller类中除了@RequestMapping映射的login3方法之外,还提供了一个userModel3方法,该方法上有一个@ModelAttribute注解。userModel3方法会先于login3方法调用,这里model属性的名称没有被指定,它由@ModelAttribute注解的userModel3方法的返回类型隐含表示,如这个方法返回User类型,那么这个model属性的名称就是user。此处find(loginname,password)方法是模拟数据库根据登录名和密码查询用户功能实现。
访问request作用域中的user对象:${requestScope.user.username}<br>
在跳转的result2.jsp中可以访问到由@ModelAttribute设置的loginname和password的值。
在浏览器中输入如下URL来测试应用:http://localhost:8080/ ,跳转到如图3.8所示页面,点击“测试@ModelAttribute注释返回具体类的方法”超链接发送请求,将跳转到loginForm3.jsp页面,如图3.11所示,输入登录名“test”,密码“123456”,单击“登录”按钮发送请求,而后将先调用userModel3方法,再调用login3方法,并跳转到result3.jsp页面,如图3.13所示。
可以看到,在request作用范围域中访问到了User对象。
4. 测试@ModelAttribute和@RequestMapping同时注释一个方法
loginForm4.jsp和loginForm2.jsp页面内容一致,读者可自行参考,此处不再赘述。
@Controller
public class ModelAttribute4Controller {
// 此时login4方法的返回值并不是一个视图名称,而是model属性的值,视图名称是@RequestMapping的value值“/login4"。
// Model的属性名称由@ModelAttribute(value="")指定,相当于在request中封装了username(key)=admin(value)。
@RequestMapping(value = "/login4")
@ModelAttribute(value = "username")
public String login4() {
return "admin";
}
}
在ModelAttribute4Controller中,@ModelAttribute和@RequestMapping同时注释一个方法,此时login4方法的返回值并不是一个视图名称,而是model属性的值,视图名称是@RequestMapping的value值“/login4”。Model的属性名称由@ModelAttribute的value值指定,这相当于在request中封装了username(key)=admin(value)。
注意,此处login4方法跳转的结果是“/login4”。
访问request作用域中的username对象:${requestScope.username}<br>
在浏览器中输入如下URL来测试应用:http://localhost:8080/ ,跳转到如图3.8所示页面,点击“测试@ModelAttribute和@RequestMapping同时注释一个方法”超链接发送请求,将跳转到loginForm4.jsp页面,如图3.11所示,输入登录名“test”,密码“123456”,单击“登录”按钮发送请求,将调用login4方法,并跳转到login4.jsp页面,如图3.14所示。
可以看到,在request作用域中访问到了username的值,也就是login4方法的返回值“admin”。
5. 测试@ModelAttribute注释一个方法的参数
loginForm5.jsp和loginForm2.jsp页面内容一致,读者可自行参考,此处不再赘述。
@Controller
public class ModelAttribute5Controller {
// model属性名称就是value值,即"user",model属性对象就是方法的返回值。
@ModelAttribute("user")
public User userModel5(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password) {
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
return user;
}
// @ModelAttribute("user") User user注释方法参数,参数user的值就是userModel5()方法中的model属性。
@RequestMapping(value = "/login5")
public String login5(@ModelAttribute("user") User user) {
user.setUsername("管理员");
return "result5";
}
}
ModelAttribute5Controller类中除了@RequestMapping映射的login5方法之外,提供了一个userModel5方法,该方法上有一个@ModelAttribute(“user”)注解。userModel5方法会先于login5调用,这里model属性名称就是value值,即“user”,model属性对象就是userModel5方法的返回值User。
login5方法的参数User使用了@ModelAttribute(“user”)注解,表示参数user的值就是userModel5()方法中的model属性。
访问request作用域中的user对象:${requestScope.user.username}<br>
在浏览器中输入如下URL来测试应用:http://localhost:8080/ ,跳转到如图3.8所示页面,点击“测试@ModelAttribute注释一个方法的参数”超链接发送请求,将跳转到loginForm5.jsp页面,如图3.11所示,输入登录名“test”,密码“123456”,单击“登录”按钮发送请求,将调用login5方法,并跳转到login5.jsp页面,如图3.15所示。
可以看到,在request作用范围域中访问到了User对象。
小结:
@ModelAttribute注解的使用方法有很多中,非常灵活,读者可以根据业务需求选择使用。
3.4 信息转换
3.4.1 HttpMessageConverter<T>接口
HttpMessageConverter<T>是Spring 3.0之后新增的一个重要接口,它负责将请求信息转换为一个对象(类型为T),并将对象(类型为T)绑定到请求方法的参数中或输出为响应信息。
DispatcherServlet默认已经装配了RequestMappingHandlerAdapter作为HandlerAdapter组件的实现类,即HttpMessageConverter由RequestMappingHandlerAdapter使用,将请求信息转换为对象,或将对象转换为响应信息。
HttpMessageConverter<T>接口中定义了以下几个方法:
- boolean canRead(Class<?>clazz,MediaType mediaType)。 该方法指定转换器可以读取的对象类型,即转换器可将请求信息转换为clazz类型的对象,同时指定支持的MIME类型(text/html、application/json等)。MIME媒体类型在RFC2616中定义,具体请参考http://tools.ietf.org/html/rec2626#section-3.7。
- boolean canWrite(Class<?>clazz,MediaType mediaType)。 该方法指定转换器可以将clazz类型的对象写到响应流当中,响应流支持的媒体类型在mediaType中定义。
- List<MediaType>getSupportedMediaTypes()。该方法返回当前转换器支持的媒体类型。
- T read(Class<?extends T>clazz,HttpInputMessage inputMessage)。 该方法将请求信息转换为T类型的对象。
- void write(T t,MediaType contentType,HttpOutputMessage outputMessage)。 该方法将T类型的对象写到响应流当中,同时指定响应的媒体类型为contentType。
Spring 为HttpMessageConverter<T>提供了多个实现类,这些实现类组成了一个功能强大、用途广泛的信息转换家族。详细说明如下:
- StringHttpMessageConverter。 将请求信息转换为字符串。泛型T为String类型,可以读取所有媒体类型(
*/*
)的请求信息,可通过设置supportedMediaTypes属性指定媒体类型。响应信息的媒体类型为text/plain(即Content-Type的值)。 - FormHttpMessageConverter。 将表单数据读取到MultiValueMap中。泛型T为org.springframework.util.MultiValueMap<String,?>类型,支持读取application/x-www-form-urlencoded的类型,但不支持读取multipart/form-data的类型。可以写application/x-www-form-urlencoded及multipart/form-data类型的响应信息。
- XmlAwareFormHttpMessageConverter。继承自FormHttpMessageConverter,如果部分表单属性是XML数据,则可用该转换器进行转换。
- ResourceHttpMessageConverter。 读写org.springframework.core.io.Resource对象。泛型T为org.springspringwork.core.io.Resource对象,可以读取所有媒体类型(
*/*
)的请求信息。如果类路径下提供了JAF(Java Activation Framework),则根据Resource类型指定响应的类型,否则响应的类型为application/octet-stream。 - BufferedImageHttpMessageConverter。 读写BufferedImage对象。泛型T为BufferedImage对象,可以读取所有类型(
*/*
)的请求信息,返回BufferedImage相应的类型,也可以通过contentType显式指定。 - ByteArrayHttpMessageConverter。 读写二进制数据。泛型T为byte[]类型,可以读取所有类型(
*/*
)的请求信息,可以通过设置supportMediaTypes属性指定类型,响应信息的媒体类型为application/octet-stream。 - SourceHttpMessageConverter。 读写javax.xml.transform.Source类型的数据。泛型T为javax.xml.transform.Source类型及其扩展类,包括javax.xml.transform.dom.DOMSource、javax.xml.transform.sax.SAXSource及javax.xml.transform.stream.StreamSource,可以读取text/xml和application/xml类型请求,响应信息的类型为text/xml和application/xml。
- MarshallingHttpMessageConverter。 通过Spring 的org.springframework.oxm.Marshalling(将Java对象转换成XML)和Unmarshaller(将XML解析为Java对象)读取XML消息。泛型T为Object类型,可以读取text/xml和application/xml类型请求,响应信息的类型为text/xml和application/xml。
- Jaxb2RootElementHttpMessageConverter。 通过JAXB2读取XML消息,将请求消息转换到注解XmlRootElement和XmlType作用的类中。泛型T为Object类型,可以读取text/xml和application/xml类型请求,响应信息的类型为text/xml和application/xml。
- MappingJackson2HttpMessageConverter。 利用Jackson开源类包读取JSON数据。泛型T为Object类型,可以读取applicaion/json类型的数据,响应信息的类型为application/json。
- RssChannelHttpMessageConverter。 能够读写RSS种子消息。泛型T为com.sun.syndication.feed.rss.Channel类型,可以读取application/rss+xml类型的数据,响应信息类型为application/rss+xml。
- AtormFeedHttpMessageConverter。 能够读写RSS种子消息。泛型T为com.sum.syndication.feed.atom.Feed类型,可以读取application/atom+xml类型的数据,响应信息的类型为application/atom+xml。
RequestMappingHandlerAdapter默认已经装配了一下的HttpMessageConverter:
- StringHttpMessageConverter
- ByteArrayHttpMessageConverter
- SourceHttpMessageConverter
- XmlAwareFormHttpMessageConverter
如果需要装配其他类型的HttpMessageConverter,则可以在Spring的Web容器的上下文中自行定义一个RequestMappingHandlerAdapter,如下所示:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
</list>
</property>
</bean>
提示:
如果在Spring Web容器中显式定义了一个RequestMappingHandlerAdapter,则Spring MVC的RequestMappingHandlerAdapter默认装配的HttpMessageConverter将不再起作用。