一、注解式控制器简介
1.Spring2.5引入注解式控制器支持,通过@Controller和@RequsetMapping注解定义我们的处理器类。并且提供了一组强大的注解:
需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller和@RequestMapping注解的解释器。
- @Controller:用于标识是处理器类
- @RequestMapping:请求到处理器功能方法的映射规则
- @RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定
- @ModelAttribute:请求参数到命令对象的绑定
- @SessionAttributes:用于声明session级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session中
- @InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对象类型
2.Spring3.0引入RESTful架构风格支持(通过@PathVariable注解和一些其他特性支持),且又引入了更多的注释支持:
- @CookieValue:cookie数据到处理器功能处理方法的方法参数上的绑定
- @RequestHeader:请求头(header)数据到处理器功能处理方法的方法参数上的绑定
- @RequestBody:请求的body体的绑定(通过HttpMessageConverter进行类型转换)
- @ResponseBody:处理器功能处理方法的返回值作为响应体(通过HttpMessageConverter进行类型转换)
- @ResponseStatus:定义处理器功能处理方法/异常处理器返回的状态码和原因
- @ExceptionHandler:注解式声明异常处理器
- @PathVariable:请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI
3.Spring3.1使用新的HandlerMapping和HandlerAdapter来支持@Controller和@RequestMapping注解处理器。
新的@Controller和@RequestMapping注解支持类:处理器映射RequestMappingHandlerMapping和处理器适配器RequestMappingHandlerAdapter组合来代替之前的类。
- URI路径映射:使用URI映射请求到处理器的功能处理方法
- 请求方法映射限定:如限定功能处理方法只处理GET请求
- 请求参数映射限定:如限定只处理包含“adc”请求参数的请求
- 请求头映射限定:如限定只处理“Accept=application/json”的请求
二、具体的使用
1.控制器实现
package cn.javass.chapter6.web.contorller;
@Controller //或@RequestMapping //将一个POJO类声明为处理器
public class HelloWorldController{
@RequestMapping(value="/hello") //请求URI到处理器功能处理方法的映射
public ModelAndView helloworld(){
//1.收集参数
//2.绑定参数到命令对象
//3.调用业务对象
//4.选择下一个页面
ModelAndView mv=new ModelAndView();
//添加数据模型,可以是任意的POJO对象
mv.addObject("message","Hello World!");
//设置逻辑视图名,视图解析器会根据名字解析到具体的视图页面
mv.setViewName("hello");
return mv; //模型数据和逻辑视图名
}
}
可以通过在一个POJO类上放置@Controller或@RequestMapping,即可把一个POJO类变身为处理器。@RequestMapping(value="/hello")表示请求URI(/hello)到处理器的功能处理方法的映射。返回模型数据和逻辑视图名。
2.Spring配置文件chapter6-servlet.xml
(1)HandlerMapping和HandlerAdapter的配置
<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
(2)视图解析器的配置
<!-- viewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
(3)处理器的配置
<!-- 处理器 -->
<bean class="cn.javass.chapter6.web.controller.HelloWorldCounter"/>
只需要将处理器实现类注册到Spring配置文件即可,Spring的DefaultAnnotationHandlerMapping或RequestMappingHandlerMapping能根据注解@Controller或@RequestMapping自动发现
(4)视图页面(/WEB/INF/jsp/hello.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello World</title>
</head>
<body>
${message}
</body>
</html>
${message}:表示显示由HelloWorldController处理器传过来的模型数据。
(5)启动服务器测试
地址栏输入http://localhost:9080/springmvc-chapter6/hello,我们将看到页面显示“Hello World!”,表示成功了。
三、运行流程
四、处理器定义
1、@Controller
@Controller
public class HelloWorldController{
......
}
推荐使用这种方式声明处理器,它和我们的@Service、@Repository很好的对应了我们常见的三层开发框架
2.@RequestMapping
@RequestMapping
public class HelloWorldController{
...
}
这种方式也是可以的,但如果在类上使用@RequestMapping注解一般是用于窄化功能处理方法的映射的。
package cn.javass.chapter6.web.controller;
@Controller
@RequestMapping(value="/user") //①处理器的通用映射前缀
public class HelloWorldController2{
@RequestMapping(value="/hello2") //②相对于①出的映射进行窄化
public ModelAndView helloworld(){
//实现省略
}
}
①类上的@RequestMapping(value="/user")表示处理器的通用请求前缀
②处理器功能方法上的是对①出映射的窄化
因此http://localhost:9080/springmvc-chapter6/hello2 无法映射到HelloWorldController2的 helloWorld功能处理方法;而http://localhost:9080/springmvc-chapter6/user/hello2是可以的。
窄化请求映射还有其他方式,如在类级别指定URL,而方法级别指定请求方法类型或参数等等,后面会详细介绍
五、请求映射
处理器定义好了,那接下来我们应该定义功能处理方法,接受用户请求处理并选择视图进行渲染。
http请求信息包含六部分:
- ①请求方法:如GET或POST,表示提交的方式
- ②URL:请求的地址信息
- ③协议及版本
- ④请求头信息(包括cookie信息)
- ⑤回车换行(CRLF)
- ⑥请求内容区(即请求的内容或数据),如表单提交时的参数数据,URL请求参数(?abc=123?后边的)等等。
那此时我们可以看到有①、②、④、⑥一般是可变的,因此我们可以映射这些请求到处理器的功能处理方法,因此请求的映射分为如下几种:
- URL路径映射:使用URL映射请求到处理器的功能处理方法
- 请求方法映射限定:如限定功能处理方法只处理GET请求
- 请求参数映射限定:如限定只处理包含“adc”请求参数的请求
- 请求头映射限定:如限定只处理“Accept=application/json”的请求
1.URL路径映射
(1)普通的URL路径映射
@RequestMapping(value={"/test1","/user/create"}),多个URL路径可以映射到同一个处理器的功能处理方法。
(2)URL模板模式映射
@RequestMapping(value="/users/{userId}"),{xxx}占位符,请求的URL可以是“/users/12345”或“/users/abcd”。
通过@PathVariable可以提取URL模板模式中的{xxx}中的xxx变量。
@RequestMapping(value=“/users/{userId}/topics/{topics}”):这样也是可以的,请求的URL可以是“/users/123/topics/123”.
(3)Ant分格的URL路径映射
@RequestMapping(value="/users/**"):可以匹配“users/abc/adc”,但“/users/123”将会被URL模板模式映射中的“/users/{userId}”模式优先映射到。
@RequestMapping(value="/product?"):可匹配“/product1”或“/producta”,但不匹配“/product”或“/productaa”;
@RequestMapping(value="/product*"):可匹配“/productabc”或“/product”,但不匹配“/productabc/abc”;
@RequestMapping(value="/product/*"):可匹配“/product/abc”,但不匹配“/productabc”;
@RequestMapping(value="/products/**/{productId}"):可匹配“/products/abc/abc/123”或“/products/123”,也就是Ant风格和URI模板变量风格可混用;
2.请求方法映射限定
一般我们熟悉的表单一般分为两步:第一步展示,第二步提交。
(1)请求方法映射限定
我们熟知的,展示表单一般为GET请求方法,提交表单一般为POST请求方法。但第一部分讲的URL路径映射方式对任意请求方法是全盘接受的,因此我们需要某种方式来告知相应的功能处理方法只处理如GET请求方法的请求或POST请求方法的请求。
接下来我们使用@RequestMapping来实现SimpleFormController的功能吧。
package cn.javass.chapter6.web.controller.method;
@Controller
@RequestMapping("/customers/**") //①处理器的通用映射前缀
public class RequestMethodController{
@RequestMapping(value="/create",method=RequestMethod.GET) //②类级别的@RequestMapping窄化
public String showForm(){
System.out.println("===============GET");
return "customer/create";
}
@RequestMapping(value="/create",method=RequestMethod.POST) //③类级别的@RequestMapping窄化
public String submit(){
System.out.println("================POST");
return "redirect:/success";
}
}
①处理器的通用映射前缀(父路径):表示该处理器只处理匹配"/customers/**"的请求。
②对类级别的@RequestMapping进行窄化,表示showForm可处理匹配"customer/**/create"且请求方法为“GET”的请求。
③对类级别的@RequestMapping进行窄化,表示submit可处理匹配"/customer/**/create"且请求方法为“POST”的请求。
(2)组合使用时“或”的关系
@RequestMapping(value="/methodOr",method={RequestMethod.POST ,RequestMethod.GET):即请求方法可以是GET或POST。
3.请求参数数据映射限定
(1)请求数据中有指定参数名
package cn.javass.chapter6.web.controller.parameter;
@Controller
@RequestMapping("/parameter1") //①处理器的通用映射前缀
public class RequestParameterController1{
//②进行类级别的@RequestMapping窄化
@RequestMapping(params="create",method=RequestMethod.GET)
public String shwoForm(){
System.out.println("=============showForm"):
return "parameter/create";
}
//③进行类级别的@RequestMapping窄化
@RequestMapping(params="create",method=RequestMethod.POST)
public String submit(){
System.out.println("=============submit");
return "redirect:/success";
}
}
②@RequestMapping(params="create",method=RequestMethod.GET):表示请求中有“create”的参数名且请求方法为“GET”即可匹配,如可匹配的请求URL"http://xxx/parameter1?create"。
③@RequestMapping(params="create",method=RequestMethod.POST):表示请求中有“create”的参数名且请求方法为"POST"即可匹配。
此处的create请求参数名表示你请求的动作,即你想要的功能的一个标识。常见的CRUD我们可以使用如下请求参数来表达:
◇(create请求参数名 且 GET请求方法) 新增页面展示、(create请求参数名 且 POST请求方法)新增提交;
◇(update请求参数名 且 GET请求方法) 新增页面展示、(update请求参数名 且 POST请求方法)新增提交;
◇(delete请求参数名 且 GET请求方法) 新增页面展示、(delete请求参数名 且 POST请求方法)新增提交;
◇(query请求参数名 且 GET请求方法) 新增页面展示、(query请求参数名 且 POST请求方法) 新增提交;
◇(list请求参数名 且 GET请求方法) 列表页面展示;
◇(view请求参数名 且 GET请求方法) 查看单条记录页面展示。
(2)请求数据中没有指定参数名
//请求参数中不包含create参数名
@RequestMapping(params="!create",method=RequestMethod.GET); //进行类级别的@RequestMapping窄化
@RequestMapping(params="!create",method=RequestMethod.GET):表示请求中没有“create”参数名且请求方法为“GET”即可匹配。如可匹配的请求URL“http://xxx/parameter1?abc”。
(3)请求数据中指定参数名=值
package cn.javass.chapter6.web.controller.parameter;
//省略import
@Controller
@RequestMapping("/parameter2") //①处理器的通用映射前缀
public class RequestParameterController2 {
//②进行类级别的@RequestMapping窄化
@RequestMapping(params="submitFlag=create", method=RequestMethod.GET)
public String showForm() {
System.out.println("===============showForm");
return "parameter/create";
}
//③进行类级别的@RequestMapping窄化
@RequestMapping(params="submitFlag=create", method=RequestMethod.POST)
public String submit() {
System.out.println("===============submit");
return "redirect:/success";
}
}
②@RequestMapping(params="submitFlag=create", method=RequestMethod.GET):表示请求中有“submitFlag=create”请求参数且请求方法为“GET”即可匹配,如请求URL为http://×××/parameter2?submitFlag=create。
③@RequestMapping(params="submitFlag=create", method=RequestMethod.POST):表示请求中有“submitFlag=create”请求参数且请求方法为“POST”即可匹配。
此处的submitFlag=create请求参数表示你请求的动作,即你想要的功能的一个标识,常见的CRUD(增删改查)我们可以使用如下请求参数名来表达:
◇(submitFlag=create请求参数名 且 GET请求方法) 新增页面展示、(submitFlag=create请求参数名 且 POST请求方法) 新增提交;
◇(submitFlag=update请求参数名 且 GET请求方法) 新增页面展示、(submitFlag=update请求参数名 且 POST请求方法) 新增提交;
◇(submitFlag=delete请求参数名 且 GET请求方法) 新增页面展示、(submitFlag=delete请求参数名 且 POST请求方法) 新增提交;
◇(submitFlag=query请求参数名 且 GET请求方法) 新增页面展示、(submitFlag=query请求参数名 且 POST请求方法) 新增提交;
◇(submitFlag=list请求参数名 且 GET请求方法) 列表页面展示;
◇(submitFlag=view请求参数名 且 GET请求方法) 查看单条记录页面展示。
(4)请求数据中指定参数名!=值
//请求参数submitFlag不等于create
@RequestMapping(params="submitFalg!=create",method=RequestMethod.GET)
@RequestMapping(params="submitFlag!=create", method=RequestMethod.GET):表示请求中的参数“submitFlag!=create”且请求方法为“GET”即可匹配,如可匹配的请求URL“http://×××/parameter1?submitFlag=abc”。
(5)组合使用是“且”的关系
@RequestMapping(params={"test1", "test2=create"}) //②进行类级别的@RequestMapping窄化
@RequestMapping(params={"test1", "test2=create"}):表示请求中的有“test1”参数名 且 有“test2=create”参数即可匹配,如可匹配的请求URL“http://×××/parameter3?test1&test2=create。
4.请求头数据映射限定
(1)请求头数据中有指定参数名
@RequestMapping(value="/header/test1",headers="Accept"):表示请求的URL必须为“/header/test1”,且请求头中必须有Accept参数才能匹配。
@RequestMapping(value="/header/test1",headers="abc"):表示请求的URL必须为"/header/test1",且请求头中必须有abc参数才能匹配。
(2)请求头中没有指定参数名
@RequestMapping(value="/header/test2",headers="!abc"):表示请求的URL必须为"/header/test2",且请求头中必须没有abc参数才能匹配。
(3)请求头数据中指定参数名=值
@RequestMapping(value="/header/test3",headers="Context-Tyep=application/json"):表示请求的URL必须为"/header/test3"且请求头中必须有"Content-Type=application/json"参数即可匹配。
当你请求的URL为"/header/test3"但如果请求头中没有或不是"Content-Type=application/json"参数,将返回“HTTP Status 415”状态码。
(4)请求头数据中指定参数名!=值
@RequestMapping(value="/header/test7", headers = "Accept!=text/vnd.wap.wml"):表示请求的URL必须为“/header/test7” 且 请求头中必须有“Accept”参数但值不等于“text/vnd.wap.wml”即可匹配。
(5)组合使用是“且”的关系
@RequestMapping(value="/header/test8", headers = {"Accept!=text/vnd.wap.wml","abc=123"}):表示请求的URL必须为“/header/test8” 且 请求头中必须有“Accept”参数但值不等于“text/vnd.wap.wml”且 请求中必须有参数“abc=123”即可匹配。