第十一章 Spring MVC的核心类和注解
11.1 DispatcherServlet
DispatcherServlet 是 Spring MVC 框架中的核心组件,它的全限定名是 org.springframework.web.servlet.DispatcherServlet。作为 Spring MVC 的前端控制器,DispatcherServlet 可以拦截客户端的请求,并根据一定的规则将请求转发给其他组件进行处理。这样做的好处是降低了 Spring MVC 各组件之间的耦合性,增强了框架的灵活性和可扩展性。
在进行 DispatcherServlet 的配置和映射时,可以通过在 web.xml 文件中进行配置。具体操作如下:
- 在项目的 web.xml 文件中添加以下配置代码:
<servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/dispatcher-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
这段代码定义了一个名为 dispatcherServlet 的 Servlet。其中,servlet-class 属性指定了 DispatcherServlet 的全限定名,init-param 可以指定 DispatcherServlet 的初始化参数,这里指定了配置文件的路径。load-on-startup 属性指定了 Servlet 的加载顺序,这里设置为 1,表示在容器启动时就加载该 Servlet,值越小越先被加载,负数被请求时才加载。
- 在 web.xml 文件中添加以下配置代码:
<servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
这段代码定义了将 / 路径的请求映射到名为 dispatcherServlet 的 Servlet 上。
通过以上配置,我们可以将 DispatcherServlet 映射到项目的根路径上,当客户端发送请求时,DispatcherServlet 就会拦截并进行相应的转发处理。需要注意的是,这里还需要在项目的 WEB-INF 目录下创建一个名为 dispatcher-servlet.xml 的配置文件,用于配置 Spring MVC 的相关组件和属性。
简单来说,就是在 web.xml 文件中配置 DispatcherServlet,并将其映射到项目的根路径上,以便进行请求的转发和处理。
11.2 @Controller注解
在 Spring MVC 的执行流程中,DispatcherServlet 接收到用户的请求后,会将请求转发给处理器类中的 Handler 进行处理。传统的处理器类需要直接或间接地实现 Controller 接口,然后在 Spring MVC 配置文件中定义请求和 Controller 的映射关系,这种方式在处理大量请求时会比较繁琐且灵活性较低。
为了解决这个问题,Spring MVC 提供了 @Controller 注解。使用 @Controller 注解标注普通的 Java 类后,该类就成为了 Spring MVC 的处理器类,无需实现 Controller 接口,并且可以通过其他注解(如 @RequestMapping、@GetMapping 等)来定义请求路径和请求方式。此外,Spring 的扫描机制可以自动扫描标注了 @Controller、@Service、@Repository 等注解的类,并将它们注册为 Spring 的 Bean,从而实现自动装配。
因此,使用 @Controller 注解可以提高 Spring MVC 处理请求的灵活性和开发效率,避免了手动配置请求和 Controller 的映射关系的繁琐过程。
以下是一个简单的使用 @Controller 注解的示例:
@Controller
@RequestMapping("/hello")
public class HelloController {@GetMapping("/world")public String helloWorld(Model model) {model.addAttribute("message", "Hello, World!");return "hello";}}
这个示例中,使用 @Controller 注解标注了一个名为 HelloController 的类,表示它是一个 Spring MVC 的处理器类。@RequestMapping 注解用于定义该处理器类的根路径为 /hello。@GetMapping 注解用于定义处理 GET 请求的 /hello/world 路径,当用户访问该路径时,会执行 helloWorld() 方法进行处理。在该方法中,使用 Model 将一个名为 message 的属性添加到模型中,并返回一个名为 hello 的视图。
当用户访问 http://localhost:8080/hello/world 时,该请求会被 DispatcherServlet 转发给 HelloController 类进行处理。helloWorld() 方法会将一个名为 message 的属性添加到模型中,并返回一个名为 hello 的视图。最终,Spring MVC 会将该视图渲染成 HTML 页面,并将其返回给用户。
为了让 Spring MVC 能够找到处理器类,需要在 Spring MVC 的配置文件中添加相应的扫描配置信息。
首先,在 Spring MVC 的配置文件中需要引入 spring-context 声明,以支持配置文件中使用的 context:component-scan 元素。通过使用 context:component-scan 元素指定被 Spring 扫描的类包,从而让 Spring MVC 能够自动扫描标注了 @Controller、@Service、@Repository 等注解的类,并将它们注册为 Spring 的 Bean,以便在需要时进行自动注入。
Spring MVC 配置文件的类包扫描配置信息可以通过以下示例进行配置:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 使用 <context:component-scan> 元素指定被 Spring 扫描的类包 --><context:component-scan base-package="com.example.controller"/></beans>
在这个示例中,使用 context:component-scan 元素指定了被 Spring 扫描的类包为 com.example.controller,表示 Spring MVC 会自动扫描该包及其子包下标注了 @Controller 注解的类,并将它们注册为 Spring 的 Bean。
11.3 @RequestMapping注解
在 Spring MVC 中使用 @Controller 注解可以将普通的类声明成处理器类,但是仅仅使用 @Controller 注解并不能让 Spring MVC 框架确定当前 Web 请求由哪个 Handler 进行处理。为了解决这个问题,Spring MVC 提供了 @RequestMapping 注解,它可以为 Handler 提供必要的映射信息,将请求的 URL 映射到具体的处理方法上。
11.3.1 @RequestMapping注解的使用
@RequestMapping 注解用于建立请求 URL和 Handler 之间的映射关系,该注解可以标注在方法上和类上。下面分别对@RequestMapping 注解的这两种使用方式进行介绍。
1. 标注在方法上
当 @RequestMapping 注解标注在方法上时,该方法就成了一个可以处理客户端请求的处理器(Handler),它会在 Spring MVC 接收到对应的 URL 请求时被执行。Handler 在浏览器中对应的访问地址由项目前缀路径和处理方法的映射路径共同组成。
在 chapter11 项目的 src/main/java 目录下创建类包 com.itheima.controller,并在类包下创建 FirstController 类。FirstController 类中创建 sayHello() 方法,用于处理客户端请求。具体代码如下:
package com.itheima.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
@RequestMapping("/hello")
public class FirstController {@RequestMapping("/sayHello0")@ResponseBodypublic String sayHello() {return "Hello, Spring MVC!";}
}
在这个示例中,@Controller 注解标注在类上,用于将该类声明为 Spring MVC 中的 Controller 类。@RequestMapping 注解标注在类上,用于指定类级别的 URL 前缀为 /hello。
sayHello() 方法是一个处理器方法,它使用 @RequestMapping 注解标注,用于将请求映射到该方法上。@RequestMapping(“/sayHello0”) 表示该方法处理 /hello/sayHello0 请求。@ResponseBody 注解用于指定返回值是响应体内容而不是视图名称。
总之,FirstController 类中的 sayHello() 方法是一个处理器方法,用于处理客户端请求。它可以通过 @RequestMapping 注解将请求映射到该方法上,并且通过 @ResponseBody 注解指定返回值为响应体内容。
2. 标注在类上
当 @RequestMapping 注解标注在类上时,@RequestMapping 的 value 属性值相当于该处理器类的命名空间,即访问该处理器类下的任意处理器都需要带上这个命名空间。@RequestMapping 标注在类上时,它的 value 属性值作为请求 URL 的第一级访问目录。
当处理器类和处理器都使用 @RequestMapping 注解指定了对应的映射路径时,处理器在浏览器中的访问地址由项目访问路径、处理器类的映射路径和处理器的映射路径共同组成。
下面通过一个案例演示 @RequestMapping 注解标注在类上的使用。修改文件 11-3,在 FirstController 类上标注 @RequestMapping 注解,具体代码如下:
package com.itheima.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
@RequestMapping("/hello")
public class FirstController {@RequestMapping("/sayHello0")@ResponseBodypublic String sayHello() {return "Hello, Spring MVC!";}
}
在这个示例中,@RequestMapping 注解标注在类上,用于指定该处理器类的命名空间为 /hello。也就是说,访问该处理器类下的任意处理器都需要带上 /hello 这个命名空间。
同时,sayHello() 方法上的 @RequestMapping(“/sayHello0”) 注解也指定了该方法的映射路径为 /sayHello0。因此,访问该方法的完整 URL 为 /项目访问路径/hello/sayHello0。
总之,@RequestMapping 注解标注在类上时,可以为该处理器类下的所有处理器方法指定一个命名空间,从而在访问该处理器类下的任意处理器方法时都需要带上这个命名空间。
11.3.2 @RequestMapping注解的属性
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
value | String[] | {} | 请求 URL 映射路径,支持 Ant 风格的路径表达式 |
method | RequestMethod[] | {} | 请求方法 |
params | String[] | {} | 请求参数 |
headers | String[] | {} | 请求头 |
consumes | String[] | {} | 请求体的 MediaType |
produces | String[] | {} | 响应体的 MediaType |
path | String[] | {} | 请求 URL 映射路径,等同于 value 属性 |
name | String | “” | 处理器方法的名称,用于生成文档 |
description | String | “” | 处理器方法的描述,用于生成文档 |
-
@RequestMapping 注解的主要属性是 value 和 method。其中,value 属性用于指定请求 URL 的映射路径,可以支持 Ant 风格的路径表达式;method 属性用于指定请求方法,支持 HTTP 的 GET、POST、PUT、DELETE 等请求方法。
-
params 属性用于指定请求参数,可以使用表达式来判断请求参数是否符合条件。例如,params={“username”,“age!=18”} 表示请求必须携带 username 参数,并且 age 参数不能等于 18。
-
headers 属性用于指定请求头,可以使用表达式来判断请求头是否符合条件。例如,headers={“Content-Type=application/json”} 表示请求头必须包含 Content-Type 并且值为 application/json。
-
consumes 属性用于指定请求体的 MediaType,例如 consumes={“application/json”} 表示请求体的 MediaType 必须是 application/json。
-
produces 属性用于指定响应体的 MediaType,例如 produces={“application/json”} 表示响应体的 MediaType 将是 application/json。
-
path 属性等同于 value 属性,只是为了提高代码可读性而设置的别名。
-
name 和 description 属性用于生成文档,可以根据业务需要来设置。
1. value属性
value 属性是 @RequestMapping 注解的默认属性,用于指定请求 URL 的映射路径。当 value 属性是 @RequestMapping 注解显式使用的唯一属性时,可以省略 value 的属性名,例如 @RequestMapping(value=“/firstController”) 可以简写为 @RequestMapping(“/firstController”)。
使用 value 属性时,可以指定映射单个的请求 URL,也可以将多个请求映射到一个方法上。例如,@RequestMapping(value = {“/users”, “/members”}) 表示将 /users 和 /members 两个请求路径都映射到对应的处理器方法上。
下面是一个示例代码,其中 @RequestMapping 注解的 value 属性用于指定请求 URL 的映射路径:
@Controller
@RequestMapping("/example")
public class ExampleController {// 映射单个请求 URL@RequestMapping("/hello")@ResponseBodypublic String hello() {return "Hello, World!";}// 映射多个请求 URL@RequestMapping(value = {"/users", "/members"})@ResponseBodypublic String getUsers() {return "List of users";}
}
在上述代码中,@RequestMapping(“/example”) 将类 ExampleController 的所有处理器方法的 URL 映射路径都添加了 /example 的命名空间,即访问该类下的任意处理器方法都需要加上 /example。@RequestMapping(“/hello”) 将 hello() 方法的 URL 映射路径设置为 /example/hello,即访问 /example/hello 将调用 hello() 方法;@RequestMapping(value = {“/users”, “/members”}) 将 getUsers() 方法的 URL 映射路径设置为 /example/users 和 /example/members,即访问 /example/users 或 /example/members 将调用 getUsers() 方法。
2. method属性
method 属性可以对处理器方法映射的 URL 请求方式进行限定。当请求的 URL 和处理器映射成功,但请求方式和 method 属性指定的属性值不匹配时,处理器将无法正常处理请求。
下面是一个示例代码,其中 @RequestMapping 注解的 method 属性用于限定处理器方法支持的 HTTP 请求方法:
@Controller
@RequestMapping("/example")
public class ExampleController {// 映射 GET 请求@RequestMapping(value = "/hello", method = RequestMethod.GET)@ResponseBodypublic String hello() {return "Hello, World!";}// 映射 POST 请求@RequestMapping(value = "/hello", method = RequestMethod.POST)@ResponseBodypublic String helloPost() {return "Hello, POST!";}
}
在上述代码中,@RequestMapping(“/example”) 将类 ExampleController 的所有处理器方法的 URL 映射路径都添加了 /example 的命名空间,即访问该类下的任意处理器方法都需要加上 /example。@RequestMapping(value = “/hello”, method = RequestMethod.GET) 将 hello() 方法的 URL 映射路径设置为 /example/hello,并且只允许 GET 请求访问该 URL;
@RequestMapping(value = “/hello”, method = RequestMethod.POST) 将 helloPost() 方法的 URL 映射路径设置为 /example/hello,并且只允许 POST 请求访问该 URL。
假设客户端发送一个 POST 请求到 /example/hello,则只有 helloPost() 方法能够正常处理该请求,而 hello() 方法将不能处理该请求,因为它只支持 GET 请求。
总之,使用 method 属性可以确保处理器方法只能接收特定的 HTTP 请求方法,以提高应用程序的安全性和稳定性。
3. params属性
params 属性可以用来限制处理器方法的请求参数,确保只有满足特定的请求参数限制才能访问处理器方法。具体来说,params 属性中定义的值可以将请求映射的定位范围缩小。当客户端进行请求时,如果请求参数的值等于 params 属性定义的值,则可以正常执行所映射到的方法;否则映射到的方法不执行。
下面是一个示例代码,其中 @RequestMapping 注解的 params 属性用于限制处理器方法的请求参数:
@Controller
@RequestMapping("/example")
public class ExampleController {// 映射请求参数中包含 id=10 的 GET 请求@RequestMapping(value = "/users", params = "id=10", method = RequestMethod.GET)@ResponseBodypublic String getUserById() {return "User with id=10";}// 映射请求参数中包含 name=John 的 POST 请求@RequestMapping(value = "/users", params = "name=John", method = RequestMethod.POST)@ResponseBodypublic String createUserWithName() {return "User created with name=John";}
}
在上述代码中,@RequestMapping(“/example”) 将类 ExampleController 的所有处理器方法的 URL 映射路径都添加了 /example 的命名空间,即访问该类下的任意处理器方法都需要加上 /example。@RequestMapping(value = “/users”, params = “id=10”, method = RequestMethod.GET) 将 getUserById() 方法的 URL 映射路径设置为 /example/users,只有当请求参数中包含 id 参数且值为 10 时,才能访问该处理器方法;@RequestMapping(value = “/users”, params = “name=John”, method = RequestMethod.POST) 将 createUserWithName() 方法的 URL 映射路径设置为 /example/users,只有当请求参数中包含 name 参数且值为 John 时,才能访问该处理器方法。
假设客户端发送一个 GET 请求到 /example/users,并且请求参数中包含 id 参数且值为 10,则只有 getUserById() 方法能够正常处理该请求,而 createUserWithName() 方法将不能处理该请求,因为它只支持 POST 请求且请求参数中必须包含 name 参数且值为 John。
总之,使用 params 属性可以确保处理器方法只能接收特定的请求参数,以提高应用程序的安全性和稳定性。
11.3.3 请求映射方式
基于注解风格的 Spring MVC 通过 @RequestMapping 注解指定请求映射的 URL 路径。常见的 URL 路径映射方式包括以下三种:
-
基于请求方式的 URL 路径映射:通过 @RequestMapping 注解的 method 属性将处理器方法映射到特定的 HTTP 请求方法上。例如,@RequestMapping(value = “/example”, method = RequestMethod.GET) 将处理器方法映射到 GET 请求上。
-
基于 Ant 风格的 URL 路径映射:通过 @RequestMapping 注解的 value 属性和 ?、*、** 等特殊字符来实现路径匹配和通配符匹配。例如,@RequestMapping(value = “/example/**”) 将处理器方法映射到以 /example/ 开头的所有请求路径上。
-
基于 REST 风格的 URL 路径映射:通过 @RequestMapping 注解的 value 属性和 {} 占位符来实现 REST 风格的 URL 路径映射。例如,@RequestMapping(value = “/example/{id}”, method = RequestMethod.GET) 将处理器方法映射到以 /example/ 开头,后面跟着一个变量名为 id 的请求路径上。在处理器方法中,可以通过 @PathVariable 注解将 URL 路径中的变量值注入到方法参数中。
1. 基于请求方式的URL路径映射
什么是基于请求方式的 URL 路径映射?
基于请求方式的 URL 路径映射是指将处理器方法映射到特定的 HTTP 请求方法上,例如 GET、POST、PUT、DELETE 等。在 Spring MVC 中,可以使用 @RequestMapping 注解的 method 属性来指定处理器方法所映射的 HTTP 请求方法。
如何使用基于请求方式的 URL 路径映射?
可以使用 @RequestMapping 注解的 method 属性来指定处理器方法所映射的 HTTP 请求方法。例如,@RequestMapping(value = “/example”, method = RequestMethod.GET) 表示将处理器方法映射到 GET 请求上,而 @RequestMapping(value = “/example”, method = RequestMethod.POST) 则表示将处理器方法映射到 POST 请求上。
除了使用 @RequestMapping 注解的 method 属性,还可以使用以下几种缩写方式来指定 HTTP 请求方法:
-
@GetMapping:表示将处理器方法映射到 GET 请求上,等价于 @RequestMapping(method = RequestMethod.GET)。
-
@PostMapping:表示将处理器方法映射到 POST 请求上,等价于 @RequestMapping(method = RequestMethod.POST)。
-
@PutMapping:表示将处理器方法映射到 PUT 请求上,等价于 @RequestMapping(method = RequestMethod.PUT)。
-
@DeleteMapping:表示将处理器方法映射到 DELETE 请求上,等价于 @RequestMapping(method = RequestMethod.DELETE)。
示例代码
以下是一个使用基于请求方式的 URL 路径映射的示例代码:
@RestController
@RequestMapping("/api")
public class ApiController {// GET 请求,查询用户信息@GetMapping("/users/{id}")public User getUser(@PathVariable("id") Long id) {// 根据 id 查询用户信息return userService.getUserById(id);}// POST 请求,创建用户信息@PostMapping("/users")public User createUser(@RequestBody User user) {// 创建用户信息return userService.createUser(user);}// PUT 请求,更新用户信息@PutMapping("/users/{id}")public User updateUser(@PathVariable("id") Long id, @RequestBody User user) {// 更新用户信息return userService.updateUser(id, user);}// DELETE 请求,删除用户信息@DeleteMapping("/users/{id}")public void deleteUser(@PathVariable("id") Long id) {// 删除用户信息userService.deleteUser(id);}
}
在以上示例代码中,我们定义了一个名为 ApiController 的 RESTful 风格的 API 控制器类,其中包含了 4 个处理器方法,分别对应着 GET、POST、PUT、DELETE 四种 HTTP 请求方法。这些处理器方法使用了不同的 HTTP 请求方法映射注解,例如 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping,来指定处理器方法所映射的 HTTP 请求方法。同时,这些处理器方法也指定了不同的 URL 路径,例如 /api/users/{id}、/api/users 等,用于表示不同的 API 操作和资源路径。
2. 基于Ant风格的URL路径映射
Ant 风格的 URL 路径映射是一种常用的 URL 路径匹配方式,它使用 Ant 风格的通配符来匹配 URL 路径。在 Spring MVC 中,可以使用 @RequestMapping 注解的 value 属性来指定处理器方法所映射的 URL 路径,并使用 Ant 风格的通配符来进行路径匹配。
Ant 风格的通配符包括三种:
- ?:匹配任意单个字符。
- *:匹配任意多个字符,包括 0 个字符。
- **:匹配任意多级目录,包括 0 级目录。
在使用 Ant 风格的 URL 路径映射时,可以在 @RequestMapping 注解的 value 属性中使用 Ant 风格的通配符来匹配 URL 路径。例如,@RequestMapping(“/example/?”) 可以匹配 /example/1、/example/2 等路径,而 @RequestMapping(“/example/*”) 可以匹配 /example/abc、/example/123 等路径。另外,@RequestMapping(“/example/**”) 可以匹配 /example、/example/subpath、/example/subpath/subsubpath 等路径。
当映射路径中同时使用多个通配符时,可能会发生通配符冲突的情况,例如多个通配符同时匹配同一个请求路径的情况。在这种情况下,Spring MVC 会采用最长匹配原则来解决通配符冲突,即会优先匹配最长的通配符,以保证匹配规则的准确性。
具体来说,如果一个请求路径同时满足两个或多个 Ant 风格的映射路径匹配规则,那么请求路径最终会匹配满足规则字符最多的路径。例如,对于请求路径 /ant/a/path,如果存在以下两个映射路径规则:
/**/path
/ant/*/path
那么最终匹配的路径规则会是第二个规则,即 /ant/*/path,因为它具有更长的匹配字符集。
3. 基于RESTful风格的URL路径映射
除了支持 Ant 风格的 URL 路径映射,Spring MVC 还支持 RESTful 风格的 URL 路径映射。REST(Representational State Transfer)是一种基于 HTTP 协议的网络资源访问风格,它规范了网络资源的访问方式,每个网络资源都有一个唯一的 URI(Uniform Resource Identifier)来标识。
RESTful 风格的 URL 路径映射将请求参数转化为请求路径的一部分,采用名词形式的路径,例如 /user/id/1,其中 user 表示用户,id 表示用户的唯一标识符。
RESTful 风格在 HTTP 请求中通过 GET、POST、PUT 和 DELETE 这四个动词对应四种基本请求操作:
-
GET:用于获取资源,即查询资源的信息。例如,使用 GET 请求获取用户信息的 URL 可以是 /users/1,其中 1 表示用户的唯一标识符。
-
POST:用于新建资源,即在服务器上创建新的资源。例如,使用 POST 请求创建新用户的 URL 可以是 /users,请求体中包含新用户的信息。
-
PUT:用于更新资源,即更新服务器上已有的资源。例如,使用 PUT 请求更新用户信息的 URL 可以是 /users/1,请求体中包含更新后的用户信息。
-
DELETE:用于删除资源,即删除服务器上已有的资源。例如,使用 DELETE 请求删除用户的 URL 可以是 /users/1,其中 1 表示要删除的用户的唯一标识符。
需要注意的是,RESTful 风格中的 URL 应该具有可读性和意义性,能够清晰地反映请求的资源。同时,RESTful 风格中的 URL 也应该遵循 HTTP 协议的语义,即使用合适的 HTTP 方法对资源进行操作。
RESTful 风格4种请求的约定方式如表所示。
URL 路径 | 请求方式 | 操作 | 说明 |
---|---|---|---|
/resource/{id} | GET | 读取(Retrieve) | 用于获取指定资源的详细信息 |
/resource | POST | 创建(Create) | 用于创建新的资源 |
/resource/{id} | PUT | 更新(Update) | 用于更新指定资源的详细信息 |
/resource/{id} | DELETE | 删除(Delete) | 用于删除指定资源 |