目录
5.@RequestMapping 配合 @PathVariable映射:
四、@RequestMapping延伸——SpringMVC中,如何通过注解实现POJO类直接作为Controller,而不依赖Servlet或接口?
一、@RequestMapping是什么?
(1) @RequestMapping注解 用于将浏览器发来的 HTTP 请求映射到具体的 Controller类 或者 某个方法上。
(2) @RequestMapping 定义了请求的 URL 路径、请求使用的 HTTP 方法(GET, POST 等)、请求的参数、请求头等匹配条件。
(3) @RequestMapping 既可以在类级别使用,定义指定类的所有方法共享的基础路径;也可以在方法级别使用,定义具体处理请求的路径;还可以同时在类和方法级别上使用,并且,当同时修饰类和方法时,请求的 url 就是它们的组合—— /类请求值/方法请求值,具体应该为——http://IP[域名]:port/WEB工程路径/类请求值/方法请求值。
二、@RequestMapping 的使用演示
1.@RequestMapping在方法上的使用:
我们在 “SpringMVC 执行流程分析” 一文中已经演示过 @RequestMapping 在方法上的使用了,具体请见链接文章的 “快速入门” 部分。如下图所示:
2.@RequestMapping同时在类和方法上使用:
我们在 “SpringMVC 执行流程分析” 一文中已经配置过 applicationContext-mvc.xml 和 web.xml。
这里以 “购买商品并提示购买成功” 的小demo进行演示:先来准备一个 Controller 或者说是 Handler,OrderHandler代码如下:
package com.cyan.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author : Cyan_RA9
* @version : 22.0
*/
@RequestMapping(value = "/order")
@Controller
public class OrderHandler {
/**
* 1. method=RequestMethod.POST: 表示请求 purchase 目标方法的请求方式必须是 post
* 2. SpringMVC 控制器默认支持 GET 和 POST 两种方式,(如果不指定默认就是这两个)
*/
@RequestMapping(value = "/purchase", method = RequestMethod.POST)
public String purchase() {
System.out.println("make a purchase of goods~~~");
return "purchase_OK";
}
}
可以看到,OrderHandler 和 purchase() 方法上面都用了 @RequestMapping 进行修饰,如果想访问 purcahse方法,正确的 URL 就应该是 http://localhost:8080/SpringMVC/order/purchase。
注意,此处我们将 @RequestMapping 的method属性指定为了 POST,其实一共有八种类型(常用的有GET,POST,PUT,DELETE,HEAD这些),如下图所示:
通过一个 form 表单来访问这个URL,并且指定为 post 类型,testPurchase.jsp代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>BUY_GOODS</title>
<style>
/* 给表格和所有单元格设置边框 */
table, th, td {
border: 1px solid black;
}
table {
border-collapse: collapse;
padding: 2px;
}
th {
font-weight: bold;
border: 2px solid blue;
}
</style>
</head>
<body>
<form action="order/purchase" method="post"> <!-- order前面不带/ -->
<table class="my-table">
<tr>
<th colspan="2">Buy Goods</th>
</tr>
<tr>
<td>buyerName: </td>
<td><input type="text" name="buyerName"/></td>
</tr>
<tr>
<td>amount: </td>
<td><input type="text" name="amount"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Purchase"/></td>
</tr>
</table>
</form>
</body>
</html>
表单效果图如下:
再来一个 purchase_OK 页面,对应 OrderHandler 的purchase()方法的 return "purchase_OK"。purchase_OK.jsp代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Successfully</title>
</head>
<body>
<h3>Purchase Successfully!</h3>
</body>
</html>
purchase_OK 页面效果图如下:
最后进行运行测试,运行结果如下GIF图所示:
注意,由于我们在OrderHandler中指定了请求方式为 POST类型,所以如果我们将 form表单 的method属性改为 get,如下图所示:
此时form表单的请求类型和要访问的purchase()方法指定的请求类型不一致,将会报错405,如下图所示:
3.@RequestMapping指定请求参数:
@RequestMapping 中的 params 属性可以用于指定请求参数,如下图所示:
params 具体的使用方式有下面几种——
① params = "param1_name" : 表示请求必须包含名称是 "param1_name" 的请求参数。
② params = "param1_name!=value1":表示请求必须包含名称是 "param1_name" 的请求参数 并且 它的值不可以是 value1(手动指定)。
③ params = "!=param1_name":表示请求必须 不包含 参数名称为 "param1_name" 的请求参数。
④ params = { "param1_name=value1", "param2_name" } :表示请求必须同时包含名称为 "param1_name" 和 "param2_name" 的请求参数 并且 param1_name 参数的值必须是指定的 value1。
以 “保存订单” 的demo来测试 params 的具体用法。在OrderHandler中新增一个saveOrder 方法,OrderHandler类代码如下:
package com.cyan.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author : Cyan_RA9
* @version : 22.0
*/
@RequestMapping(value = "/order")
@Controller
public class OrderHandler {
/**
* 1. method=RequestMethod.POST: 表示请求 purchase 目标方法的请求方式必须是 post
* 2. SpringMVC 控制器默认支持 GET 和 POST 两种方式,(如果不指定默认就是这两个)
*/
@RequestMapping(value = "/purchase", method = RequestMethod.POST)
public String purchase() {
System.out.println("make a purchase of goods~~~");
return "purchase_OK";
}
@RequestMapping(value = "/save", params = "orderId!=0", method = RequestMethod.GET)
public String saveOrder(String orderId) {
// 传入的参数会传递到方法的形参列表
System.out.println("orderId = " + orderId);
return "order_OK";
}
}
对应的order_OK.jsp代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SuccessfullyEX</title>
</head>
<body>
<h3 style="color: cornflowerblue">Order Successfully!</h3>
</body>
</html>
再来一个用于测试 /order/save 的form表单,testOrder.jsp代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SAVE_ORDERS</title>
<style>
/* 给表格和所有单元格设置边框 */
table, th, td {
border: 1px solid black;
}
table {
border-collapse: collapse;
padding: 2px;
}
th {
font-weight: bold;
border: 2px solid blue;
}
</style>
</head>
<body>
<form action="order/save" method="get">
<table class="my-table">
<tr>
<th colspan="2">Make an Order</th>
</tr>
<tr>
<td>orderId: </td>
<td><input type="text" name="orderId"/></td>
</tr>
<tr>
<td>orderType: </td>
<td><input type="text" name="orderType"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="ORDER"/></td>
</tr>
</table>
</form>
</body>
</html>
页面效果如下图所示:
我们先输入符合规则的orderId,测试效果如下GIF图所示:
但是,如果我们将 orderId 改成 0,就会报错400,如下GIF图所示:
4.@RequestMapping使用Ant风格URL:
@RequestMapping 支持下面三种 Ant 风格 URL——
① "?" :匹配文件名中的任意一个字符;
② "*" :匹配文件名中的任意1到多个字符(不能匹配空字符串);
③ "**":匹配多层路径(0到多个路径段)。
这里up在之前用过的UserSerlvet上做做手脚,来测试 Ant风格 URL,UserServlet代码如下:
package com.cyan.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller //@Controller注解,标识当前类是一个控制器,有时也称为Handler(处理器)
public class UserServlet {
@RequestMapping(value = "/user/?/login") //此处的value可以省略
public String login() {
System.out.println("This is login!");
return "login_OK";
}
@RequestMapping(value = "/user/*/sign") //此处的value可以省略
public String sign() {
System.out.println("This is sign~~~");
return "login_OK";
}
@RequestMapping(value = "/user/**/register") //此处的value可以省略
public String register() {
System.out.println("This is register+++");
return "login_OK";
}
}
再来一个 testAnt.jsp 用于测试URL的匹配情况,testAnt.jsp代码如下——
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>testAnt</title>
</head>
<body>
<a href="user/1/login">login_test1</a> <br/>
<a href="user/2/login">login_test2</a> <br/>
<a href="user/3/login">login_test3</a> <br/> <br/>
<a href="user/abc/sign">sign_test1</a> <br/>
<a href="user/fff/sign">sign_test2</a> <br/>
<a href="user/Cyan-RA9/sign">sign_test3</a> <br/> <br/>
<a href="user/register">register_test1</a> <br/>
<a href="user/Pro/register">register_test2</a> <br/>
<a href="user/Pro/Max/Ultra/Plus/register">register_test3</a>
</body>
</html>
最后进行运行测试,测试情况如下 GIF图 所示:
IDEA 后台的输出结果如下图所示:
正好对应了 UserServlet 中三个方法的输出语句,符合预期。
5.@RequestMapping 配合 @PathVariable映射:
一般情况下,URL的参数形式是——action 属性[+?+请求参数]。其中,请求参数的格式是:name=value&name=value。
但是,借助 @PathVariable 可以省略参数名,简化URL.
新建一个 UserHandler 用于演示,UserHandler类代码如下:
package com.cyan.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author : Cyan_RA9
* @version : 22.0
*/
@Controller
@RequestMapping(value = "/user")
public class UserHandler {
/*
1. @RequestMapping 中定义的参数名和 @PathVariable 中指定的参数名 必须保持一致。
2. 使用了 @PathVariable 的方法的形参名,可以与上面两者不一致,形参名无所谓。
*/
@RequestMapping(value = "/sign/up/{username}/{userid}")
public String sign_up(@PathVariable("username") String name, @PathVariable("userid") String id) {
System.out.println(("Parameters Received : " + "username = " + name + ", userid = " + id));
return "login_OK";
}
}
再来一个 testPathVariable 页面,用于测试 URL 的简化效果,testPathVariable.jsp 代码如下——
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>@PathVariable Test</title>
</head>
<body>
<h3>Click the reference to take a test</h3>
<a href="user/sign/up/Cyan_RA9/141">点我就完事儿!</a>
</body>
</html>
测试结果如下GIF图所示:
三、@RequestMapping的使用细节
1. @RequestMapping(及其派生注解)映射的 URL 不能重复,即同一个项目中不能有两个方法去匹配完全相同的 URL,这时候 Tomcat 启动时会报错,如下图所示:
2.@RequestMapping(value = "/xxx",method = RequestMethod.POST) 就等价于 @PostMapping(value = "/xxx") 。类似的写法还有: @GetMapping,@PutMapping,@DeleteMapping。
3.如果已经确定 某个表单 或者 超链接 会提交字段数据, 那么要求提交的参数名 和 目标方法的参数名保持一致.(也就是我们在上面演示3 和 演示5中做的那样.)
四、@RequestMapping延伸——SpringMVC中,如何通过注解实现POJO类直接作为Controller,而不依赖Servlet或接口?
我们可以使用 @Controller 来把一个 POJO类 标记为SpringMVC的 控制器,@Controller 本身可以看作是 @Component 的一个特例,所以被 @Controller 标记的类也会被 Spring 容器作为组件进行管理。除了 @Controller 之外,我们还需要另一个核心注解——@RequestMapping(或者它的派生注解),这个注解可以把浏览器发来的 HTTP 请求映射到具体的 Controller 类 或者 某个方法上面;@RequestMapping 直接定义了 HTTP 请求的一些属性,例如请求的URL,请求的方法类型,请求的参数等等。
那么上面说的这两个注解,就是我们自己 能操作的部分,但是真正要想实现对 Servlet API 的解耦,背后靠的是 前端控制器(DispatcherServlet),处理器映射器(HandlerMapping),处理器适配器(HandlerAdapter) 这三大组件的协同工作,尤其是 HandlerAdapter(因为它直接实现了 适配器模式)。DispatcherServlet 拿到 HandlerMapping 找到的处理器(即 我们标记的 POJO Controller)后,并不会直接调用它。而是将处理器交给 HandlerAdapter,让它去调用。HandlerAdapter 会从 HttpServletRequest 中提取信息(即参数解析),然后把提取到的有用信息 适配为 POJO 方法的参数,那么我们标记为控制器的 POJO,根本就不需要直接接触 HttpServletRequest 或 HttpServletResponse。而且 HandlerAdapter 通过反射直接检查 POJO 方法签名,只要方法签名符合 Spring MVC 的约定,就可以被调用,所以也不需要依赖接口。
SpringMVC的执行流程回顾——如下图所示:
五、总结
- 🆗,以上就是 SpringMVC --- @RequestMapping 的全部内容了😀。
- 这篇文章没什么难度,主要就是演示了一下 @RequestMapping 的各种使用方式和技巧,大家只要知道 @RequestMapping 在整个 Spring MVC 中很重要并且把它用熟练就可以了。