控制器的基本使用
在 Spring MVC 中,在 POJO 类定义注解
@Controller ,再在mvc配置文件中通过
<context:component-scan /> 扫描相应的类包,就可以使一个 POJO 类变成一个可以处理 HTTP 请求的控制器(具体代码见:http://blog.youkuaiyun.com/al_assad/article/details/79012665);
用户可以创建数量不限的控制器,用于分别处理不同的业务请求,每个控制器拥有多个处理请求的方法;
使用 @RequestMapping 映射请求
@RequestMapping 注解用于将请求映射到对应的控制器方法中,这个过程包含一系列的映射规则,这些映射规则包括4个方面的信息:
请求URL、请求参数、请求方法、请求头;
@RequestMapping 的 value 、method、params、headers 参数分别表示映射条件:请求URL、请求方法、请求参数、请求报文头;
1)通过 URL 映射
public class UserController {
"/user/createUser") // 等同于 @RequestMapping(path="/user/createUser") (
public String createUser(){
....
return "user/createSuccess";
}
}
以上 createUser 方法响应 “Web应用部署路径/user/createUser” 的HTTP请求,如:
“
http://www.assad.site/user/createUser”
;
可以在控制器头标注基础 @RequestMapping,以下示例中 createUser 响应 “
Web应用部署路径/user/createUser ” 请求;
"/user") //以下所有 @RequestMapping 的响应 url 基于该 @RequestMapping 定义的 url (
public class UserController {
"/createUser") //响应 "/user/createUser" (
public String createUser(){
....
return "user/createSuccess";
}
}
@RequestMapping 除了支持标准的URL之外,还支持
带【 ?、*、**】 占位符的Ant 风格的 URL 和
带 {xxx} 占位符的 URL
/user/*/createUser:匹配类似 “user/aa/createUser”;/user/**/createUser:匹配类似 “user/aa/bbb/createUser”,“user/createUser”;/user/createUser??:匹配类似 “user/createUseraa”;
/user/{userId}:匹配类似 “user/123”;/company/{companyId}/user/{userId}:匹配类似 “/company/123/user/12345”;
其中
{xxx} 占位符可以使用
@PathVariable 标注绑定的变量名;
"/user") (
public class UserController {
"/{userId}") (
public String showDetail( ("userId") String userId){
System.out.println(userId); //如果请求为 "/user/201502012",则 userId = "201502012"
...
return ...;
}
}
2)通过请求方法、请求参数,请求报头映射
"/user") (
public class UserController {
/*使用请求方法和请求参数映射请求
以下例子映射一个GET请求: /user/test1?userId=xxx */
path="/test1",method=RequestMethod.GET,params="userId") (
public String test1 ( ("userId") String userId){ //使用 @RequestParam 标注请求参数
System.out.println(userId);
...
return "user/test1";
}
/*使用报头映射请求*/
path="/test2",headers="content-type=text/*") (
public String test2(){
....
return "user/deleteUserSuccess";
}
}
对于使用请求方法、请求参数映射,可以使用
@RequestParam 对请求参数绑定到方法入参中;
params、headers 参数接受简单表达式,以 params 示例:
params="userId" :请求参数为 “userId”;params="!userId" :请求参数不为 “userId”;params="userId!=123" :请求参数为 “userId” 且参数值不为 123;params={"userId","gameId"} :请求参数为 “userId”,“gameId”;
请求处理方法签名
- 入参:Spring MVC 对控制器方法请求的限制是很宽松的,必要时可以对方法入参标注相应的注解(如:@PathVariable、@RequestParam、@RequestHeader 等);
- 返回值:一般处理方法的返回值类型为ModelAndView或 String,ModelAndView 包含模型和逻辑视图名,String 代表一个逻辑视图名;
① 使用 @RequestParam 绑定请求参数值
@RequestParam 注解包含以下参数:
- value:参数名;
- required:是否必须,默认为 true;
- defaultValue:默认参数名,不常用;
path="/handle1",method= RequestMethod.POST,params={"userName","password"}) (
public String handle( ("userName") String userName,
value="password", required=false) String passowrd){ (
User user = new User(userName,passowrd);
.....
}
② 使用 @CookieValue 绑定请求中的 Cookie 值
"/handle2") (
public String handle( ("userid") String userId){
......
}
③ 使用 @Request 绑定请求报文头的属性值
"/handle3") (
public String handle( ("Accept-Language") String acceptLanguage){
.......
}
④ 使用命令/表单对象绑定请求的参数值
命令/表单对象不需要实现任何接口,仅仅是一个拥有若干属性的POJO,以下代码中 User 为一个命令/表单对象,类结构为
User(userName,password)
;
假设响应的 “/handle4” 请求具体为
"/handle4?userName=assad&password=1234",以下方法会将其正确映射为一个
User(userName=“assad”,password="1234");
"/handle4") (
public ModelAndView handle2(User user){
return new ModelAndView("success","user",user);
}
⑤ 使用 Servlet API 对象作为入参
在 Spring MVC 中,控制器类可以不依赖于任何 Servlet API 对象,但是可以将这些 Servlet API 对象绑定到入参中;
"/handle5") (
public String handle3(HttpServletRequest request, ("userId") String userId){
request.getSession().setAttribute("userId",userId); //通过 HTTPServletRequest 对象设置 Session
return "success";
}
⑥ 使用 I/O 对象作为入参
Spring MVC 允许控制器的处理方法使用 java.io.InputStream / java.io.Reader,java.io.OutputStream / java.io.Writer 作为方法入参,Spring MVC 将获取 ServletRequest 的 InputStream/Reader,ServletReponse 的 OutputStream/Writer ,然后传递给控制器的处理方法;
RequestMapping("/handle5")
public void handle5(OutputStream out) throws IOException {
Resource resource = new ClassPathResource("/image/imageTest.jpg"); //复制图片文件给ServletRequest的输出流
FileCopyUtils.copy(resource.getInputStream(),out);
}
模型数据处理
Spring MVC 提供了以下多种途径输出模型:
- ModelAndView:当处理方法返回值类型为ModelAndView时,方法体可以通过该对象添加模型数据;
- @ModelAttribute:在方法入参标注该注解后,入参的对象就会放置在该数据模型中;
- Map 和 Model:如果方法入参为 org.springframework.ui.Model、org.springframework.ui.ModelMap、java.util.Map 时,当处理方法返回时,Map中的数据会自动添加到模型中;
- @SessionAttribute:将模型中的某个属性暂存在 HttpSession 中,以便多个请求共享该属性;
① ModelAndView
ModelAndView 包含视图信息和模型数据,可以简单把它的数据模型看成一个 Map<String,Object> 对象;
"/handle4") (
public ModelAndView handle2(User user){
ModelAndView mav = new ModelAndView();
mav.setViewName("success");
mav.addObject("user",user);
return mav;
}
//或者
"/handle4") (
public ModelAndView handle2(User user){
ModelAndView mav = new ModelAndView("success");
mav.addObject("user",user);
return mav;
}
//或者
"/handle4") (
public ModelAndView handle2(User user){
return new ModelAndView("success","user",user);
}
Spring MVC 会把 ModelAndView 的数据模型放置到
ServletRequest 中,在
JSP页面获取该数据模型有以下的方式:
<%--通过EL表达式的Request域隐含对象获取--%>
<h1>your name is <c:out value="${user.name}"/> </h1>
<h1>your name is ${user.name} </h1>
<%--通过JSP隐式对象获取--%>
<h1>your name is <c:out value="request.getAttribute('user').getName()"/> </h1>
注意:由于ModelAndView中的模型数据在经过Web服务器时,可能由于Web服务器的字符编码和程序编码不统一,会造成类似中文字符乱码,有一个解决方式是将字符串放置到模型时强制编码格式,如下:
"/handle4") (
public ModelAndView handle2(User user){
if(user == null)
return new ModelAndView("fail","error",new String("用户对象为空值".getBytes(),"UTF-8"));
//将中文字符强制转为与程序编码相同的”UTF-8“
else
return new ModelAndView("success","user",user);
}
② @ModelAttribute
@ModelAttribute 注解可以直接将方法入对象直接添加到模型中;
"/handle4") (
public String handle7( ("user") User user){
user.setId("20130602613");
return "success";
}
除了在控制器控制方法入参中使用 @ModelAttribute 之外,还可以在方法定义中使用该注解,Spring MVC 在调用给目标处理方法之前,会先逐个调用在方法级别上标注了@ModelAttribute 的方法,
如下示例:
"user") (
public User getUser(){
User user = new User();
user.setId("20130602613");
return user;
}
"/handle4") (
public String handle7( ("user") User user){
return "success";
}
③ Map / Model
Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口储存模型数据,该接口的功能类似于 java.util.Map,Spring MVC 在调用方法之前会创建一个隐含的 Model 数据模型对象,作为模型数据的储存容器,
如果处理方法的入参为 Map 或 Model 类型,Spring MVC 会将该隐含模型的引用传递给这些入参;
"/handle9",params={"name","password","credits"}) (
public String handle(User user,ModelMap modelMap){
modelMap.put("user",User); //将user放置到ModelMap中
return "showUser";
}
//等同于:
"/handle9",params={"name","password","credits"}) (
public ModelAndView handle(User user){
return new ModelAndModel("showUser","user",user);
}
//等同于:
"/handle9",params={"name","password","credits"}) (
public String handle( ("user") User user){
return "showUser";
}
④ @SessionAttribute
如果需要在多个请求之间共用某个模型的属性数据,可以通过在控制器类中标注@SessionAttributes ,Spring MVC 会将模型中的对应属性暂存到 HttpSession 中;
//同个控制器内请求重定向,同时携带模型的示例
//整个路由过程:响应 "/user/handle1" 请求,输出到 "/user/showUser" 逻辑视图
"/user") (
"user") //自动将本处理器任何处理方法属性名为"user"的模型属性透明地储存到 HttpSession 中; (
public class UserController {
"/handle1") (
public String handle1( ("user") User user){ //获取到的 User 对象模型会自动添加到隐含对象user中
user.setName("assad");
return "redirect:/user/handle1"; //转发到 "/user/handle2" 请求,此时携带了隐含对象user
}
"/handle2") (
public String handle2(ModelMap modelMap, SessionStatus sessionStatus){
User user = (User)modelMap.get("user"); //读取隐含模型中"user"模型的数据
if(user != null ){
user.setName("Vancy");
sessionStatus.setComplete(); //清除本处理器对应的会话属性,即清除HttpSession中的"user"模型
}
return "/user/showUser";
}
}
请求转发和重定向
Spring MVC 对于控制器中控制方法的请求转发和重定向提供了很方便的支持,在请求路由前加上
”forward:“表明该请求为转发请求,前加上
“redirect:”表明该请求为重定向请求;
在 Spring MVC 控制器中转发请求和重定向请求的区别在于:
- 转发(forward):控制器内转发请求、接受请求的处理方法之间隐含对象共享;客户端浏览器中的页面URL不变,;
- 重定向(redirect):控制器内重定向请求、接受请求的处理方法之间隐含对象不共享;客户端浏览器中的页面URL改变为重定向后的URL;
转发(forward)
"/user") (
public class UserController {
value="/handle1",params="userId") (
public String handle1(HttpServletRequest request, ("userId") String userId){
request.setAttribute("userId",userId);
return "forward:/user/handle2"; //转发请求到"/user/handle2"
}
"/handle2") (
public String handle2(HttpServletRequest request){
return "/user/showUser"; //在"user/showUser"中 ${userId} 可以获取到"/user/handle1"的请求参数"userId"值;
}
}
重定向(redirect
)
"/handle1") (
public String redirectToBaidu(){
return "redirect:https://www.baidu.com"; //重定向到百度
}
"/handle2",method=RequestMethod.GET,params="keyword") (
public String baiduSearch( ("keyword") String keyword){
return "redirect:https://www.baidu.com?word="+keyword; //重定向到百度,并进行关键词搜索
}