Spring MVC 是 Spring 框架中用于构建 Web 应用程序的核心模块,它基于经典的 MVC (Model-View-Controller) 设计模式。本教程只针对注解开发,可以快速上手。
(一)快速入门
-
创建一个WEB项目,并导入依赖
注意:Tomcat9.0.97版本低于10使用依赖javax.servlet,但如果Tomcat版本大于10使用jakarta.servlet,即tomcat版本与spring的版本要对应,否则可能会出现一些问题
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.30</version> <!-- Spring 5 最新稳定版本 --> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies>
- 在config包下设置servlet容器的配置
public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer { /** * 创建Servlet的容器,加载springMVC对应的bean并放入其中 * @return */ @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } /** * 配置请求路径,设置为 / 表示拦截所有请求 */ @Override protected String[] getServletMappings() { return new String[]{"/"}; } /** * 如果加载Servlet容器时,需要加载非sprintMVC对应的类用到该方法 * @return */ @Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; } }
-
在config包下设置springMVC的配置类
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.cqut.controller") public class SpringMvcConfig { }
-
实现简单的controller
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/user") public class UserController { @RequestMapping(value="/save",method={RequestMethod.POST,RequestMethod.GET}) @ResponseBody public String save(){ System.out.println("springMVC..."); return "{'success':'hello'}"; } @RequestMapping("/delete") @ResponseBody public void delete(){ System.out.println("6"); } }
执行流程
(二)Bean的加载控制
一般情况springMVC对应的Bean(controller中的Bean)与spring中的Bean要分开加载,下面是两种控制Bean加载的方法。
-
精准扫描包
@Configuration @ComponentScan({"com.cqut.service","com.cqut.dao"}) public class SpringConfig { }
-
配置过滤器
@Configuration @ComponentScan( value = "com.cqut", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
配置Servlet容器的简化开发
/**
* 配置Servlet的容器
*/
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
(三)请求与响应
普通参数配置
@RequestMapping("/commonParam")
@ResponseBody
public void add(String name,int age){
System.out.println("name :" + name);
System.out.println("age :" + age);
}
处理中文乱码
在容器Servlet配置新增拦截器
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
特殊类型参数配置
注意对于json数据转换需要导入依赖并在SpringMVC配置类添加 @EnableWebMvc
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version> <!-- 最新稳定版本 -->
</dependency>
@RequestMapping("/diffParam")
@ResponseBody
public void diffParamMethod(@RequestParam("name") String userName, int age){
System.out.println("name :" + userName);
System.out.println("age :" + age);
}
// 引用类型
@RequestMapping("/pojoParam")
@ResponseBody
public void pojoParamMethod(User user){
System.out.println("age :" + user);
}
// 嵌套引用
@RequestMapping("/pojoParamDemo")
@ResponseBody
public void pojoParamMethodDemo(User user){
System.out.println("age :" + user);
}
@RequestMapping("/arrayParam")
@ResponseBody
public void arrayParamMethod(String[] likes){
System.out.println("age :" + likes);
}
@RequestMapping("/listParam")
@ResponseBody
public void listParamMethod(@RequestParam List<String> list){
System.out.println("age :" + list);
}
// 接受json数据
@RequestMapping("/arrayJsonParam")
@ResponseBody
public void arrayJsonParamMethod(@RequestBody String[] list){
System.out.println("age :" + list);
}
// 接受json数据
@RequestMapping("/listJsonParam")
@ResponseBody
public void listJsonParamMethod(@RequestBody List<User> list){
System.out.println("age :" + list);
}
// 接受json数据
@RequestMapping("/pojoJsonParam")
@ResponseBody
public void pojoJsonParamMethod(@RequestBody User user){
System.out.println("age :" + user);
}
(四)常用注解功能
注解名称 | 功能说明 | 使用示例 |
---|---|---|
@Controller | 标记一个类为 Spring MVC 控制器。负责处理 HTTP 请求,通常与方法级的 @RequestMapping 及其变体结合使用。 | @Controller public class UserController { ... } |
@RestController | @Controller 和 @ResponseBody 的组合注解。标记的类中所有方法返回值默认直接写入 HTTP 响应体(如 JSON/XML),用于构建 RESTful Web 服务。 | @RestController @RequestMapping("/api/users") public class UserApiController { ... } |
@RequestMapping | 将 HTTP 请求映射到 MVC 控制器方法。可指定 URL 路径、HTTP 方法(GET/POST/PUT/DELETE 等)、请求参数、请求头等条件。 | @RequestMapping(value = "/users", method = RequestMethod.GET) public List<User> listUsers() { ... } |
@GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping | @RequestMapping 的特定 HTTP 方法快捷方式。分别用于映射 GET、POST、PUT、DELETE、PATCH 请求。更简洁常用。 | @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { ... } @PostMapping("/users") public User createUser(@RequestBody User user) { ... } |
@PathVariable | 将 URI 模板变量绑定到方法参数。用于从 URL 路径中提取值。 | @GetMapping("/users/{userId}/orders/{orderId}") public Order getOrder( @PathVariable("userId") Long userId, @PathVariable Long orderId) { ... } // 名称匹配时可省略("orderId") |
@RequestParam | 将 HTTP 请求参数绑定到方法参数。用于获取查询字符串(?name=value )或表单数据(application/x-www-form-urlencoded )。 | @GetMapping("/search") public List<User> searchUsers( @RequestParam("name") String name, @RequestParam(value = "page", defaultValue = "1") int page) { ... } |
@RequestBody | 将 HTTP 请求体(通常是 JSON/XML)绑定到方法参数对象。常用于接收 POST/PUT 提交的复杂数据。 | @PostMapping("/users") public ResponseEntity<User> createUser( @RequestBody @Valid User newUser) { ... } // @Valid 用于触发数据校验 |
@ResponseBody | 指示方法返回值应直接写入 HTTP 响应体(而非视图解析)。通常用于返回 JSON/XML 数据。@RestController 已包含此注解。 | @GetMapping("/users/{id}") @ResponseBody // 在 @Controller 类中需要显式添加 public User getUser(@PathVariable Long id) { ... } |
@ModelAttribute | 1. 方法参数: 将请求参数绑定到命令/表单对象。 2. 方法上: 在控制器方法执行前向模型添加属性。 | 参数: @PostMapping("/submitForm") public String submitForm(@ModelAttribute("userForm") User user) { ... } 方法: @ModelAttribute("categories") public List<String> populateCategories() { ... } // 此方法在所有控制器方法前执行 |
@RequestHeader | 将 HTTP 请求头绑定到方法参数。 | @GetMapping("/someEndpoint") public String handle( @RequestHeader("User-Agent") String userAgent, @RequestHeader(value = "Accept", defaultValue = "application/json") String accept) { ... } |
@CookieValue | 将 HTTP Cookie 的值绑定到方法参数。 | @GetMapping("/dashboard") public String dashboard( @CookieValue("JSESSIONID") String sessionId) { ... } |
@SessionAttribute | 将 HTTP Session 中的属性绑定到方法参数。 | @GetMapping("/checkout") public String checkout( @SessionAttribute("cart") ShoppingCart cart) { ... } |
@Valid / @Validated | 触发对绑定到方法参数(如 @RequestBody , @ModelAttribute )的对象进行 Bean Validation (JSR 380) 校验。 | @PostMapping("/users") public ResponseEntity<?> createUser( @RequestBody @Valid User newUser, BindingResult result) { ... } // BindingResult 接收校验错误 |
(五)响应
一般情况的响应,不添加 @RequestBody
注解,会直接解析成视图(页面)
@RequestMapping(value="/save",method={RequestMethod.POST,RequestMethod.GET})
public String save(){
System.out.println("springMVC...");
return "{'success':'hello'}";
}
返回json数据直接添加 @RequestBody
注解,这个数据转换是HttpMessageConvert
// 接受json数据
@RequestMapping("/pojoJsonParam")
@ResponseBody
public User pojoJsonParamMethod(@RequestBody User user){
System.out.println("age :" + user);
return user;
}
(六)RESTful开发
REST 全称是 Representational State Transfer(表述性状态转移),对请求路径的约束,描述模块名称用复数
REST风格简介
具体原则
-
资源与 URI:
-
URI 标识资源: URI 应该表示资源本身,而不是对资源的操作。
-
好:
/users
,/users/123
,/orders/456/items
-
坏:
/getUsers
,/createUser
,/updateUser?id=123
-
-
使用名词(复数): 通常使用名词复数形式表示资源集合。
-
/users
(用户集合),/products
(产品集合)
-
-
层次关系: 使用路径表示资源间的关系。
-
/users/123/orders
(用户 123 的所有订单) -
/users/123/orders/456
(用户 123 的订单 456)
-
-
避免文件扩展名: 使用
Accept
和Content-Type
头部协商媒体类型,而不是在 URI 中用.json
或.xml
。-
好:
GET /users/123
withAccept: application/json
-
坏:
GET /users/123.json
-
-
-
HTTP 方法:
-
使用标准的 HTTP 方法来定义对资源的操作:
-
GET
: 获取资源(一个或列表)。安全(不修改资源)且幂等(多次请求结果相同)。 -
POST
: 创建新资源。不安全(修改资源)且不幂等(多次创建多个资源)。通常用于向集合提交新资源。 -
PUT
: 完整更新资源(客户端提供完整的更新后资源表述)。不安全但幂等(多次更新效果与一次相同)。通常用于更新已知 URI 的资源。 -
PATCH
: 部分更新资源(客户端只提供需要修改的字段)。不安全但幂等(需要正确实现)。用于更新资源的部分属性。 -
DELETE
: 删除资源。不安全但幂等(删除一次或多次,资源最终不存在)。 -
(较少直接使用:
HEAD
- 获取资源元信息;OPTIONS
- 获取资源支持的通信选项)
-
-
URI 与方法结合: 同一个 URI 配合不同的 HTTP 方法,执行不同的操作。
-
GET /users/123
-> 获取用户 123 的信息 -
PUT /users/123
-> 更新用户 123 的完整信息 -
DELETE /users/123
-> 删除用户 123 -
POST /users
-> 创建一个新用户(ID 通常由服务器生成)
-
-
@RequestMapping(value="/users",method=RequestMethod.POST)
@ResponseBody
public String save(){
System.out.println("springMVC...");
return "{'success':'hello'}";
}
// @Pathvvaribale 路径变量
@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
@ResponseBody
public void delete(@PathVariable Integer id){
System.out.println("id :" + id);
}
RESTful简化开发
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public void getAll(){
System.out.println("get all ...");
}
@GetMapping("/{id}")
public void getById(@PathVariable Integer id){
System.out.println(id);
}
@PostMapping
public void add(@RequestBody Book book){
System.out.println(book);
}
@PutMapping
public void update(@RequestBody Book book){
System.out.println(book);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Integer id){
System.out.println(id);
}
}
设置访问静态资源的路径
/**
* 设置静态资源访问路径
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
(七)SSM整合开发
异常处理
异常处理一般放到表现层进行,采用Aop进行同一处理
// 异常处理的注解
@RestControllerAdvice
public class ProjectExceptionAdvice {
// 定义拦截异常的类型
@ExceptionHandler(Exception.class)
public void doException(){
System.out.println("出现异常");
}
}
针对一下情况出现异常,需要自己封装异常进行处理
(八)拦截器
拦截器是 Spring MVC 框架的核心组件之一,用于在请求处理的不同阶段执行预处理和后处理操作。它类似于 Servlet 中的过滤器(Filter),但提供了更细粒度的控制。
拦截器 vs 过滤器
特性 | 拦截器 (Interceptor) | 过滤器 (Filter) |
---|---|---|
作用范围 | Spring MVC 框架内部 | Servlet 容器级别 |
依赖 | 依赖 Spring 容器 | 依赖 Servlet 容器(如 Tomcat) |
拦截目标 | 只拦截 Controller 请求 | 拦截所有请求(包括静态资源) |
控制粒度 | 可精确到 Handler 方法级别 | 较粗粒度(基于 URL 模式) |
获取信息 | 可直接获取 Handler 方法、ModelAndView 等 | 只能获取 Servlet API 对象 |
拦截器核心
@Component
public class ProjectInterceptor implements HandlerInterceptor {
// 执行controller之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler ...");
// 返回为true代表,放行原方法,
// 返回值为false代表,拦截原方法,在原方法执行之后的都不会执行
return true;
}
// 执行controller之后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandler ...");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
// 执行postHandle之后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("after ...");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
多拦截器的执行顺序
-
当配置多个拦截器时,形成拦截器链:
拦截器链是指一系列按照特定顺序排列的拦截器,它们会依次对请求进行处理。 -
拦截器链的运行顺序参照拦截器添加顺序为准:
拦截器的执行顺序与其在配置文件中的添加顺序一致。例如,如果添加了三个拦截器(pre1、pre2、pre3),它们将按此顺序执行。 -
当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行:
如果某个拦截器返回false
,表示该拦截器阻止了请求的继续处理,后续的拦截器将不再执行。例如,如果pre2返回false
,则pre3和controller及其后的拦截器都不会被执行。 -
当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作:
如果某个拦截器中断了请求处理流程,只有在该拦截器之前的拦截器的afterCompletion
方法会被调用。例如,如果pre2返回false
,则只有pre1的afterCompletion
方法会被调用。