【Spring进阶】掌握Spring MVC框架核心注解:从基础到实战应用(实战指南)
文章目录
- Spring MVC常用注解详解及实践
- 引言
- 第一部分:Spring MVC基础回顾
- 第1章:Spring MVC概述
- 第2章:MVC模式与Spring MVC
- 第3章:快速上手Spring MVC
- 第二部分:核心控制器注解
- 第4章:@Controller
- 第5章:@RestController
- 第6章:@RequestMapping
- 第三部分:请求处理注解
- 第7章:@RequestParam
- 第8章:@PathVariable
- 第9章:@ModelAttribute
- 第10章:@RequestBody 和 @ResponseBody
- 第四部分:视图与模型管理
- 第11章:@ViewResolver
- 第12章:@ModelAttribute 和 @ModelAttribute 注解
- 第13章:@SessionAttributes 和 @ModelAttribute("name")
- 第五部分:数据验证与类型转换
- 第14章:@Valid 和 @Validated
- 第15章:@InitBinder 和 @DateTimeFormat
- 第六部分:高级主题
- 第16章:@ExceptionHandler
- 第17章:@Async
- 第18章:拦截器 (@WebFilter 和 @ControllerAdvice)
Spring MVC常用注解详解及实践
引言
1. Spring MVC框架简介
Spring MVC 是 Spring Framework 的一部分,它提供了一种基于 Model-View-Controller (MVC) 设计模式的 Web 应用程序框架。Spring MVC 通过清晰的分层架构简化了 Web 开发过程,使得开发者可以更容易地构建可维护、可扩展的应用程序。
2. 注解在Spring MVC中的重要性
Spring MVC 中大量使用了注解来简化配置和代码编写。注解提供了声明式编程方式,允许开发者以更简洁、直观的方式来定义组件的行为,而无需显式配置 XML 文件。这种灵活性提高了开发效率,减少了出错的可能性。
3. 为什么学习Spring MVC注解
- 提高开发效率:使用注解可以减少配置文件的编写量,简化控制器类的定义。
- 代码可读性:注解使代码更加清晰,易于理解和维护。
- 灵活性:注解提供了灵活的方式来控制控制器行为,比如路由、数据绑定等。
- 社区支持:Spring 社区庞大,有大量的资源和工具支持注解的使用。
第一部分:Spring MVC基础回顾
第1章:Spring MVC概述
1. 架构原理
Spring MVC 的架构基于 MVC 模式,它将应用程序分为三个主要部分:
- Model(模型):包含应用程序的核心业务逻辑和数据。
- View(视图):负责展示数据,通常是由 JSP、Thymeleaf 或其他模板引擎生成。
- Controller(控制器):处理用户请求并协调模型和视图之间的交互。
2. 请求处理流程
- 用户通过浏览器向服务器发送请求。
- DispatcherServlet 作为前端控制器接收请求,并将请求转发给相应的 HandlerMapping。
- HandlerMapping 查找与请求匹配的 Controller 并返回给 DispatcherServlet。
- DispatcherServlet 将请求委托给对应的 Controller。
- Controller 处理请求后返回一个 ModelAndView 对象。
- ViewResolver 解析视图名称并返回具体的 View 实现。
- DispatcherServlet 渲染视图并将结果发送回客户端。
第2章:MVC模式与Spring MVC
1. MVC设计模式
MVC 设计模式是一种软件架构模式,它提倡将应用程序逻辑分成三个互相关联的组件:
- Model:保存应用程序的数据和业务逻辑。
- View:显示数据给用户。
- Controller:处理用户的输入,并将它转化为对 Model 或 View 的操作。
2. Spring MVC实现MVC模式的方式
Spring MVC 实现了 MVC 模式的各个方面,其中:
- Model:由 Service 层和 DAO 层实现。
- View:通常由 JSP 页面、HTML 页面或其他视图技术实现。
- Controller:由注解驱动的控制器类实现。
第3章:快速上手Spring MVC
1. 创建Spring MVC项目
- 使用 Maven 或 Gradle 创建一个新的 Java 项目。
- 添加必要的依赖,例如 Spring Web MVC、Spring Core、Spring AOP 等。
- 创建一个简单的控制器类,使用
@Controller和@RequestMapping注解。
2. 配置web.xml和spring-mvc.xml
web.xml:
<web-app><servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- Enable component scanning --><context:component-scan base-package="com.example.controller" /><!-- Configure view resolver --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/" /><property name="suffix" value=".jsp" /></bean><!-- Other configurations here -->
</beans>
第二部分:核心控制器注解
第4章:@Controller
1. 使用场景
- 标记控制器类:
@Controller注解用于标记一个类作为 Spring MVC 的控制器。 - 处理HTTP请求:控制器类中的方法通常会处理来自客户端的 HTTP 请求。
2. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloWorldController {@RequestMapping("/hello")public String sayHello() {// 返回视图名return "hello";}
}
第5章:@RestController
1. 与@Controller的区别
- 自动响应体:
@RestController相当于@Controller加上@ResponseBody,表示该控制器的所有响应都会直接写入 HTTP 响应体中,而不是返回视图名称。 - RESTful风格:
@RestController更适用于构建 RESTful Web 服务,因为它假设所有的方法都将返回 JSON 或 XML 数据。
2. 代码示例
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloWorldRestController {@GetMapping("/hello")public String sayHello() {return "Hello, World!";}
}
第6章:@RequestMapping
1. URL映射
- 路径映射:使用
value或path属性指定控制器方法与 URL 路径之间的映射关系。 - 多路径映射:可以通过指定多个值来映射多个 URL 到同一个方法。
2. HTTP方法限定
- 限定请求类型:通过
method属性指定控制器方法只能处理特定类型的 HTTP 请求(如 GET、POST)。
3. 请求参数绑定
- 绑定请求参数:可以使用
@RequestParam或直接在方法签名中使用参数名称来绑定 URL 查询字符串或表单数据中的参数到控制器方法的参数上。
4. 代码示例
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/greeting")
public class GreetingController {@GetMappingpublic String greeting(@RequestParam(name = "name", required = false, defaultValue = "World") String name) {return "Hello, " + name + "!";}
}
在这个示例中,/greeting 映射到了 GreetingController 类,而该类的方法则通过 @GetMapping 映射到 /greeting 的 GET 请求。同时,@RequestParam 注解用来从查询字符串中获取 name 参数,并将其绑定到方法参数上。
第三部分:请求处理注解
第7章:@RequestParam
1. 获取请求参数
- 基本使用:
@RequestParam注解用于从 HTTP 请求的查询字符串中获取参数值。
2. 参数默认值
- 设置默认值:如果请求中没有提供某个参数,可以为
@RequestParam设置一个默认值。
3. 参数校验
- 必填校验:使用
required属性来指定参数是否必须存在。 - 校验参数值:可以通过
defaultValue属性设置默认值,或者结合 Java Bean Validation API 进行更复杂的校验。
4. 代码示例
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.validation.constraints.NotNull;@RestController
@RequestMapping("/greeting")
public class GreetingController {@GetMappingpublic String greeting(@RequestParam(name = "name", required = true, defaultValue = "World") String name,@RequestParam(name = "age", required = false) Integer age,@RequestParam(name = "country", required = true) @NotNull String country) {if (age != null) {return "Hello, " + name + "! You are " + age + " years old and from " + country + ".";} else {return "Hello, " + name + "! You are from " + country + ".";}}
}
第8章:@PathVariable
1. 路径变量提取
- 提取路径变量:
@PathVariable注解用于从 URL 中提取路径变量。
2. 代码示例
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/users")
public class UserController {@GetMapping("/{userId}")public String getUser(@PathVariable("userId") int userId) {return "User ID: " + userId;}
}
在这个示例中,/users/{userId} 映射到了 UserController 类,而该类的方法则通过 @GetMapping 映射到 /users/{userId} 的 GET 请求。@PathVariable 注解用来从 URL 中获取 userId 参数,并将其绑定到方法参数上。
第9章:@ModelAttribute
1. 模型属性绑定
- 简单类型绑定:可以直接使用方法参数来绑定简单的请求参数。
- 复杂对象绑定:可以使用
@ModelAttribute注解来绑定复杂的对象到方法参数上。
2. 复杂对象绑定
- 自动绑定:Spring MVC 会尝试将请求参数与方法参数上的对象属性进行自动绑定。
3. 代码示例
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestController
@RequestMapping("/contact")
public class ContactController {@GetMappingpublic String getContact(@ModelAttribute Contact contact) {Map<String, Object> model = new HashMap<>();model.put("contact", contact);return "contact";}static class Contact {private String name;private String email;private String message;// Getters and setters}
}
第10章:@RequestBody 和 @ResponseBody
1. RESTful风格数据交互
- 请求体绑定:
@RequestBody注解用于将请求体中的 JSON 或 XML 数据绑定到方法参数上。 - 响应体绑定:
@ResponseBody注解用于将方法返回值直接写入到 HTTP 响应体中。
2. JSON/XML序列化
- JSON序列化:默认情况下,Spring MVC 使用 Jackson 或其他 JSON 库来序列化和反序列化 JSON 数据。
- XML序列化:可以使用 JAXB 或其他 XML 库来序列化和反序列化 XML 数据。
3. 代码示例
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@RestController
@RequestMapping("/api")
public class ApiController {@PostMapping(value = "/save", consumes = MediaType.APPLICATION_JSON_VALUE)public @ResponseBody String save(@Valid @RequestBody User user) {return "User saved with ID: " + user.getId();}@GetMapping(value = "/user/{id}", produces = MediaType.APPLICATION_JSON_VALUE)public @ResponseBody User getUser(@PathVariable("id") int id) {User user = new User(id, "John Doe", "john.doe@example.com");return user;}static class User {private int id;private String name;private String email;// Constructors, getters, and setters}
}
在这个示例中,@PostMapping 和 @RequestBody 被用来处理 POST 请求并将请求体中的 JSON 数据绑定到 User 对象上。@GetMapping 和 @ResponseBody 用来返回一个 JSON 格式的 User 对象。
第四部分:视图与模型管理
第11章:@ViewResolver
1. 视图解析器配置
- 配置视图解析器:
@ViewResolver注解不是直接用于类或方法上的,而是用来配置视图解析器的 bean。Spring MVC 使用视图解析器来解析视图名称并返回具体的视图对象。
2. 常用视图技术(JSP/Thymeleaf等)
- JSP:Java Server Pages 是一种广泛使用的视图技术。
- Thymeleaf:一种现代服务器端的 Java 模板引擎,支持 HTML5、XHTML 和 XML。
3. 代码示例
<!-- spring-mvc.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- Enable component scanning --><context:component-scan base-package="com.example.controller" /><!-- Configure view resolver for Thymeleaf --><bean id="viewResolver"class="org.thymeleaf.spring5.view.ThymeleafViewResolver"><property name="templateEngine" ref="templateEngine" /></bean><bean id="templateEngine"class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"><property name="templateMode" value="HTML5" /><property name="prefix" value="/templates/" /><property name="suffix" value=".html" /></bean><!-- Other configurations here -->
</beans>
第12章:@ModelAttribute 和 @ModelAttribute 注解
1. 模型属性管理
- 添加模型属性:
@ModelAttribute注解可以用来添加模型属性到模型中。 - 模型属性预填充:可以在控制器方法中使用
@ModelAttribute注解来预填充模型属性。
2. 数据预填充
- 预填充模型属性:在控制器方法中使用
@ModelAttribute注解来预填充模型属性。
3. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;@Controller
@RequestMapping("/form")
public class FormController {@ModelAttribute("title")public String getTitle() {return "Welcome to the Form";}@GetMappingpublic String showForm(Model model) {model.addAttribute("user", new User());return "form";}static class User {private String name;private String email;// Constructors, getters, and setters}
}
第13章:@SessionAttributes 和 @ModelAttribute(“name”)
1. 会话管理
- 会话属性:
@SessionAttributes注解用于将模型属性添加到 HTTP 会话中。 - 访问会话属性:可以通过
@ModelAttribute("name")注解来访问会话中的模型属性。
2. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;@Controller
@RequestMapping("/session")
@SessionAttributes(names = {"user"})
public class SessionController {@ModelAttribute("user")public User getUser() {return new User();}@GetMappingpublic String showForm(Model model) {return "form";}@PostMappingpublic String processForm(User user, SessionStatus sessionStatus) {System.out.println("User: " + user.getName() + ", " + user.getEmail());sessionStatus.setComplete(); // 清除会话属性return "result";}static class User {private String name;private String email;// Constructors, getters, and setters}
}
第五部分:数据验证与类型转换
第14章:@Valid 和 @Validated
1. 表单验证
- 使用
@Valid和@Validated进行验证:@Valid注解用于方法参数上,而@Validated用于类级别,两者都可以利用 Java Bean Validation API 来进行数据验证。
2. 自定义验证器
- 自定义验证器:可以创建自定义的验证器来满足特定的验证需求,通过实现
ConstraintValidator接口来实现。
3. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.validation.Valid;@Controller
@RequestMapping("/validate")
public class ValidationController {@GetMappingpublic String showForm(Model model) {model.addAttribute("user", new User());return "form";}@PostMappingpublic String processForm(@Valid User user, BindingResult result, Model model) {if (result.hasErrors()) {return "form"; // 如果有错误,则重新显示表单}model.addAttribute("message", "User details are valid!");return "result";}static class User {private String name;private String email;// @Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")private String password;// Constructors, getters, and setters}
}
在这个示例中,@Valid 注解被用于 processForm 方法的参数 User 上,以触发数据验证。如果验证失败,BindingResult 对象会包含验证错误信息,并且表单会被重新显示给用户。
第15章:@InitBinder 和 @DateTimeFormat
1. 类型转换
- 使用
@InitBinder进行类型转换:@InitBinder注解用于控制器方法上,可以注册自定义的DataBinder来处理类型转换和格式化。
2. 日期格式化
- 使用
@DateTimeFormat进行日期格式化:@DateTimeFormat注解用于方法参数上,可以指定日期和时间的格式。
3. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.validation.Valid;@Controller
@RequestMapping("/date")
public class DateController {@InitBinderpublic void initBinder(WebDataBinder binder) {binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));}@GetMappingpublic String showForm(Model model) {model.addAttribute("event", new Event());return "form";}@PostMappingpublic String processForm(@Valid Event event, BindingResult result, Model model) {if (result.hasErrors()) {return "form"; // 如果有错误,则重新显示表单}model.addAttribute("message", "Event date is valid: " + event.getDate());return "result";}static class Event {@DateTimeFormat(pattern = "yyyy-MM-dd")private Date date;// Constructors, getters, and setters}
}
在这个示例中,@InitBinder 方法注册了一个自定义的日期编辑器,用于将日期字符串转换成 java.util.Date 类型。@DateTimeFormat 注解被用于 Event 类的 date 字段上,指定了日期的格式。
第六部分:高级主题
第16章:@ExceptionHandler
1. 异常处理
- 局部异常处理:
@ExceptionHandler注解用于处理控制器类内部发生的异常。
2. 全局异常处理
- 全局异常处理:可以在控制器之外定义一个全局异常处理器来处理整个应用程序中的异常。
3. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;@Controller
@RequestMapping("/exception")
public class ExceptionController {@GetMapping("/error")public String throwError() {throw new RuntimeException("Something went wrong!");}@ExceptionHandler(value = {Exception.class})public ModelAndView handleException(Exception exception) {ModelAndView modelAndView = new ModelAndView("error");modelAndView.addObject("errorMessage", exception.getMessage());return modelAndView;}
}
第17章:@Async
1. 异步处理
- 异步方法执行:
@Async注解用于标记方法为异步执行,可以提高应用程序的响应速度和性能。
2. 代码示例
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;@Service
@EnableAsync
public class AsyncService {@Asyncpublic void performLongRunningTask() {try {Thread.sleep(5000); // 模拟长时间运行的任务} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Long running task completed.");}
}
第18章:拦截器 (@WebFilter 和 @ControllerAdvice)
1. 拦截器的作用
- 拦截请求:拦截器可以用来拦截请求并在请求处理之前或之后执行某些操作。
2. 全局拦截器配置
- 使用
@ControllerAdvice:@ControllerAdvice注解用于全局异常处理和其他全局拦截操作。 - 使用
@WebFilter:@WebFilter注解用于定义过滤器,可以对所有请求进行预处理或后处理。
3. 代码示例
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
@WebFilter(filterName = "loggingFilter", urlPatterns = "/*")
public class LoggingFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {long startTime = System.currentTimeMillis();filterChain.doFilter(request, response);long endTime = System.currentTimeMillis();System.out.println("Request processed in " + (endTime - startTime) + " ms");}
}import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
@RequestMapping("/interceptor")
public class InterceptorController {@GetMappingpublic String showPage() {return "page";}
}import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class LoggingInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Request intercepted before processing.");return true; // 继续请求处理}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Request intercepted after processing but before rendering the view.");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("Request intercepted after completion.");}
}
