1. Spring MVC框架简介
Spring MVC 是 Spring Framework 的一部分,它提供了一种基于 Model-View-Controller (MVC) 设计模式的 Web 应用程序框架。Spring MVC 通过清晰的分层架构简化了 Web 开发过程,使得开发者可以更容易地构建可维护、可扩展的应用程序。
2. 注解在Spring MVC中的重要性
Spring MVC 中大量使用了注解来简化配置和代码编写。注解提供了声明式编程方式,允许开发者以更简洁、直观的方式来定义组件的行为,而无需显式配置 XML 文件。这种灵活性提高了开发效率,减少了出错的可能性。
3. 为什么学习Spring MVC注解
1. 架构原理
Spring MVC 的架构基于 MVC 模式,它将应用程序分为三个主要部分:
2. 请求处理流程
1. MVC设计模式
MVC 设计模式是一种软件架构模式,它提倡将应用程序逻辑分成三个互相关联的组件:
2. Spring MVC实现MVC模式的方式
Spring MVC 实现了 MVC 模式的各个方面,其中:
1. 创建Spring MVC项目
@Controller
和 @RequestMapping
注解。2. 配置web.xml和spring-mvc.xml
web.xml:
<web-app>
<servlet>
<servlet-name>dispatcherservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
spring-mvc.xml:
<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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.controller" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
bean>
beans>
1. 使用场景
@Controller
注解用于标记一个类作为 Spring MVC 的控制器。2. 代码示例
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello")
public String sayHello() {
// 返回视图名
return "hello";
}
}
1. 与@Controller的区别
@RestController
相当于 @Controller
加上 @ResponseBody
,表示该控制器的所有响应都会直接写入 HTTP 响应体中,而不是返回视图名称。@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!";
}
}
1. URL映射
value
或 path
属性指定控制器方法与 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 {
@GetMapping
public String greeting(@RequestParam(name = "name", required = false, defaultValue = "World") String name) {
return "Hello, " + name + "!";
}
}
在这个示例中,/greeting
映射到了 GreetingController
类,而该类的方法则通过 @GetMapping
映射到 /greeting
的 GET 请求。同时,@RequestParam
注解用来从查询字符串中获取 name
参数,并将其绑定到方法参数上。
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 {
@GetMapping
public 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 + ".";
}
}
}
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
参数,并将其绑定到方法参数上。
1. 模型属性绑定
@ModelAttribute
注解来绑定复杂的对象到方法参数上。2. 复杂对象绑定
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 {
@GetMapping
public 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
}
}
1. RESTful风格数据交互
@RequestBody
注解用于将请求体中的 JSON 或 XML 数据绑定到方法参数上。@ResponseBody
注解用于将方法返回值直接写入到 HTTP 响应体中。2. JSON/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", "[email protected]");
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
对象。
1. 视图解析器配置
@ViewResolver
注解不是直接用于类或方法上的,而是用来配置视图解析器的 bean。Spring MVC 使用视图解析器来解析视图名称并返回具体的视图对象。2. 常用视图技术(JSP/Thymeleaf等)
3. 代码示例
<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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.controller" />
<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>
beans>
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";
}
@GetMapping
public String showForm(Model model) {
model.addAttribute("user", new User());
return "form";
}
static class User {
private String name;
private String email;
// Constructors, getters, and setters
}
}
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();
}
@GetMapping
public String showForm(Model model) {
return "form";
}
@PostMapping
public 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
}
}
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 {
@GetMapping
public String showForm(Model model) {
model.addAttribute("user", new User());
return "form";
}
@PostMapping
public 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
对象会包含验证错误信息,并且表单会被重新显示给用户。
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 {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
@GetMapping
public String showForm(Model model) {
model.addAttribute("event", new Event());
return "form";
}
@PostMapping
public 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
字段上,指定了日期的格式。
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;
}
}
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 {
@Async
public void performLongRunningTask() {
try {
Thread.sleep(5000); // 模拟长时间运行的任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Long running task completed.");
}
}
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 {
@Override
protected 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 {
@GetMapping
public 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 {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Request intercepted before processing.");
return true; // 继续请求处理
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Request intercepted after processing but before rendering the view.");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Request intercepted after completion.");
}
}