Spring MVC 框架
主要作用
Spring MVC 是一个基于 Java 的 Web 框架,主要用于构建 Web 应用程序。它的主要作用是将用户的 HTTP 请求与业务逻辑进行解耦,通过 MVC(模型 - 视图 - 控制器)模式来组织代码,使得代码结构更加清晰,便于维护和扩展。它能够处理用户的请求,调用业务逻辑层的方法,并将处理结果通过视图展示给用户。
核心组件
- DispatcherServlet
- 作为前端控制器,DispatcherServlet 是 Spring MVC 的核心。它接收所有的 HTTP 请求,并根据请求信息(如 URL、HTTP 方法等)来决定由哪一个处理器(Controller)来处理请求。它就像一个交通指挥官,负责将请求分发到合适的处理器。
- HandlerMapping
- 它的作用是将请求映射到处理器(Controller)。DispatcherServlet 在接收到请求后,会通过 HandlerMapping 来查找能够处理该请求的处理器。HandlerMapping 会根据请求的 URL、请求方法等因素来确定最合适的处理器。
- Controller(处理器)
- 它是业务逻辑的实现部分。Controller 接收 DispatcherServlet 分发的请求,执行业务逻辑处理,然后返回一个 ModelAndView 对象。ModelAndView 对象包含了模型数据和视图信息。
- ViewResolver(视图解析器)
- 它的作用是根据逻辑视图名解析成真正的视图(View),比如将视图名称解析为一个 JSP 页面路径。当 Controller 处理完请求后,会返回一个逻辑视图名,ViewResolver 会根据这个逻辑视图名找到对应的物理视图资源(如 JSP 文件)。
调用流程
- 用户发送请求到服务器,请求首先到达 DispatcherServlet。
- DispatcherServlet 通过 HandlerMapping 查找能够处理该请求的处理器(Controller)。
- HandlerMapping 找到对应的处理器后,DispatcherServlet 将请求转发给该处理器。
- 处理器(Controller)执行业务逻辑处理,处理完成后返回一个 ModelAndView 对象给 DispatcherServlet。
- DispatcherServlet 接收到 ModelAndView 对象后,根据其中的逻辑视图名,通过 ViewResolver 解析出真正的视图(如 JSP 页面)。
- 视图(如 JSP 页面)会根据 ModelAndView 对象中的模型数据进行渲染,生成最终的 HTML 页面。
- DispatcherServlet 将渲染后的 HTML 页面响应给用户。
简化参数接收
路径设计
在 Spring MVC 中,路径设计通常通过 @RequestMapping
注解来实现。这个注解可以放在类或方法上,用于指定请求的 URL 路径。例如:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/get")
public String getUser() {
// 处理请求的逻辑
return "userView";
}
}
在这个例子中,@RequestMapping("/user")
指定了类级别的路径前缀,@RequestMapping("/get")
指定了方法级别的路径。因此,完整的请求路径是 /user/get
。
参数接收
Spring MVC 提供了多种方式来接收请求参数:
-
通过方法参数接收:可以直接在方法参数中接收请求参数,Spring MVC 会自动将请求参数绑定到方法参数上。例如:
@RequestMapping("/get") public String getUser(@RequestParam("name") String name, @RequestParam("age") int age) { // 处理请求的逻辑 return "userView"; }
在这个例子中,
@RequestParam
注解用于指定请求参数的名称,Spring MVC 会将请求中的name
和age
参数绑定到方法的name
和age
参数上。 -
通过实体类接收:可以使用一个实体类来接收多个请求参数。例如:
public class User { private String name; private int age; // getter 和 setter 方法 } @RequestMapping("/get") public String getUser(User user) { // 处理请求的逻辑 return "userView"; }
在这个例子中,Spring MVC 会将请求中的
name
和age
参数绑定到User
实体类的name
和age
属性上。
请求头接收
可以通过 @RequestHeader
注解来接收请求头中的信息。例如:
@RequestMapping("/get")
public String getUser(@RequestHeader("User-Agent") String userAgent) {
// 处理请求的逻辑
return "userView";
}
在这个例子中,@RequestHeader("User-Agent")
注解用于接收请求头中的 User-Agent
信息,并将其绑定到方法的 userAgent
参数上。
Cookie 接收
可以通过 @CookieValue
注解来接收 Cookie 中的信息。例如:
@RequestMapping("/get")
public String getUser(@CookieValue("JSESSIONID") String jsessionid) {
// 处理请求的逻辑
return "userView";
}
在这个例子中,@CookieValue("JSESSIONID")
注解用于接收 Cookie 中的 JSESSIONID
信息,并将其绑定到方法的 jsessionid
参数上。
简化数据响应
模板页面
Spring MVC 支持多种模板技术,如 JSP、Velocity、Freemarker 等。可以通过返回视图名称来渲染模板页面。例如:
@RequestMapping("/get")
public String getUser(Model model) {
User user = new User("张三", 20);
model.addAttribute("user", user);
return "userView";
}
在这个例子中,Model
对象用于传递模型数据到视图页面。return "userView";
表示返回 userView
视图,Spring MVC 会通过 ViewResolver
解析出实际的视图路径(如 /WEB-INF/views/userView.jsp
),并使用模型数据渲染页面。
转发和重定向
-
转发:通过
forward:
前缀可以实现请求的转发。例如:@RequestMapping("/forward") public String forward() { return "forward:/user/get"; }
在这个例子中,请求会被转发到
/user/get
路径。 -
重定向:通过
redirect:
前缀可以实现请求的重定向。例如:@RequestMapping("/redirect") public String redirect() { return "redirect:/user/get"; }
在这个例子中,请求会被重定向到
/user/get
路径。
JSON 数据
可以通过 @ResponseBody
注解来返回 JSON 数据。例如:
@RequestMapping("/getJson")
@ResponseBody
public User getUser() {
User user = new User("张三", 20);
return user;
}
在这个例子中,@ResponseBody
注解表示方法的返回值会被转换为 JSON 数据并返回给客户端。
静态资源
Spring MVC 可以通过配置静态资源路径来处理静态资源请求。例如:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
在这个例子中,/static/**
表示所有以 /static/
开头的请求都会被映射到类路径下的 static
目录中。
RESTful 风格设计
主要作用
RESTful 风格是一种设计 Web 服务的架构风格,它通过 HTTP 方法(如 GET、POST、PUT、DELETE)来表示对资源的操作。Spring MVC 支持 RESTful 风格的设计,使得 API 设计更加简洁和直观。例如,使用 GET 方法表示获取资源,POST 方法表示创建资源,PUT 方法表示更新资源,DELETE 方法表示删除资源。
具体规范
-
GET:用于获取资源。例如:
@GetMapping("/users/{id}") public User getUser(@PathVariable("id") Long id) { // 获取用户信息 return userService.getUser(id); }
在这个例子中,
@GetMapping
注解表示这是一个 GET 请求,@PathVariable("id")
注解用于接收路径中的id
参数。 -
POST:用于创建资源。例如:
@PostMapping("/users") public User createUser(@RequestBody User user) { // 创建用户 return userService.createUser(user); }
在这个例子中,
@PostMapping
注解表示这是一个 POST 请求,@RequestBody
注解用于接收请求体中的 JSON 数据并将其绑定到User
实体类上。 -
PUT:用于更新资源。例如:
@PutMapping("/users/{id}") public User updateUser(@PathVariable("id") Long id, @RequestBody User user) { // 更新用户信息 return userService.updateUser(id, user); }
在这个例子中,
@PutMapping
注解表示这是一个 PUT 请求,@PathVariable("id")
注解用于接收路径中的id
参数,@RequestBody
注解用于接收请求体中的 JSON 数据并将其绑定到User
实体类上。 -
DELETE:用于删除资源。例如:
@DeleteMapping("/users/{id}") public void deleteUser(@PathVariable("id") Long id) { // 删除用户 userService.deleteUser(id); }
在这个例子中,
@DeleteMapping
注解表示这是一个 DELETE 请求,@PathVariable("id")
注解用于接收路径中的id
参数。
请求方式和请求参数选择
- 请求方式:根据操作的性质选择合适的 HTTP 方法。例如,获取资源使用 GET,创建资源使用 POST,更新资源使用 PUT,删除资源使用 DELETE。
- 请求参数:对于简单的查询参数,可以使用
@RequestParam
注解;对于复杂的对象参数,可以使用@RequestBody
注解;对于路径参数,可以使用@PathVariable
注解。例如:@GetMapping("/users") public List<User> getUsers(@RequestParam("name") String name) { // 根据用户名获取用户列表 return userService.getUsersByName(name); } @PostMapping("/users") public User createUser(@RequestBody User user) { // 创建用户 return userService.createUser(user); } @PutMapping("/users/{id}") public User updateUser(@PathVariable("id") Long id, @RequestBody User user) { // 更新用户信息 return userService.updateUser(id, user); } @DeleteMapping("/users/{id}") public void deleteUser(@PathVariable("id") Long id) { // 删除用户 userService.deleteUser(id); }
功能扩展
全局异常处理
可以通过 @ControllerAdvice
和 @ExceptionHandler
注解来实现全局异常处理。例如:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Map<String, Object> handleException(Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", e.getMessage());
return result;
}
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public Map<String, Object> handleUserNotFoundException(UserNotFoundException e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 404);
result.put("message", e.getMessage());
return result;
}
}
在这个例子中,@ControllerAdvice
注解表示这是一个全局异常处理器,@ExceptionHandler
注解用于指定处理的异常类型。@ResponseStatus
注解用于设置响应的状态码,@ResponseBody
注解用于返回 JSON 数据。
拦截器
可以通过实现 HandlerInterceptor
接口或继承 HandlerInterceptorAdapter
类来创建拦截器。例如:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 请求处理前的逻辑
System.out.println("preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 请求处理后的逻辑
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求完成后的逻辑
System.out.println("afterCompletion");
}
}
在 Spring 配置中注册拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/error");
}
}
在这个例子中,addInterceptors
方法用于注册拦截器,addPathPatterns
方法用于指定拦截的路径模式,excludePathPatterns
方法用于排除不需要拦截的路径。
参数校验注解
Spring MVC 支持使用 JSR 303/JSR 349 标准的注解来进行参数校验。例如:
public class User {
@NotNull(message = "用户名不能为空")
private String name;
@Min(value = 18, message = "年龄必须大于等于 18")
private int age;
// getter 和 setter 方法
}
@Controller
public class UserController {
@PostMapping("/users")
public String createUser(@Valid @ModelAttribute User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// 校验失败,返回错误信息
return "userForm";
}
// 校验成功,处理业务逻辑
userService.createUser(user);
return "redirect:/users";
}
}
在这个例子中,@NotNull
和 @Min
注解用于指定校验规则,@Valid
注解用于触发校验,BindingResult
对象用于获取校验结果。如果校验失败,bindingResult.hasErrors()
方法会返回 true
,可以返回错误信息页面;如果校验成功,继续处理业务逻辑。
一些常见的问题和解决
JSON 处理
在Spring框架中,处理JSON数据通常使用@ResponseBody
和@RequestBody
这两个注解。这两个注解在Spring MVC中用于控制HTTP请求和响应的序列化和反序列化。以下是它们的详细说明:
1. @ResponseBody
用途:
- 用于标注控制器方法,表示该方法的返回值应被作为HTTP响应体返回给客户端。Spring会自动将返回值转换为JSON或XML格式(取决于配置的
HttpMessageConverter
)。
示例:
java复制
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/user")
@ResponseBody
public User getUser() {
User user = new User();
user.setName("Alice");
user.setAge(25);
return user;
}
}
在这个示例中,getUser
方法返回一个User
对象,Spring会自动将其转换为JSON格式并返回给客户端。
2. @RequestBody
用途:
- 用于标注控制器方法的参数,表示该参数的值应从HTTP请求体中获取,并自动转换为指定的Java对象。Spring会使用
HttpMessageConverter
将请求体中的JSON或XML数据反序列化为Java对象。
示例:
java复制
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@PostMapping("/user")
public User addUser(@RequestBody User user) {
// 处理添加用户的逻辑
userService.saveUser(user);
return user;
}
}
在这个示例中,addUser
方法的参数User user
会从请求体中获取JSON数据,并自动转换为User
对象。
3. @RestController
用途:
@RestController
是一个组合注解,它包含了@Controller
和@ResponseBody
的功能。使用@RestController
标注的控制器类,其中的所有方法默认都相当于使用了@ResponseBody
注解。
示例:
java复制
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
User user = new User();
user.setName("Alice");
user.setAge(25);
return user;
}
}
在这个示例中,@RestController
注解使得getUser
方法的返回值自动作为响应体返回给客户端,无需再添加@ResponseBody
注解。
总结
- @responsebody:用于方法返回值,将返回值转换为JSON或XML格式返回给客户端。
- @RequestBody:用于方法参数,将请求体中的JSON或XML数据转换为Java对象。
- @RestController:用于控制器类,所有方法默认使用
@ResponseBody
。
这些注解在处理JSON数据时非常方便,使得Spring MVC能够自动处理HTTP请求和响应的序列化和反序列化。
静态资源访问
1. 使用web.xml
配置
在web.xml
中配置静态资源的访问路径,可以将某些路径的请求直接映射到静态资源目录。例如:
xml复制
<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>
<!-- 静态资源访问配置 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
2. 使用Spring MVC的WebMvcConfigurer
接口
在Spring Boot项目中,可以实现WebMvcConfigurer
接口来配置静态资源的访问路径。例如:
java复制
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
在这个配置中,/static/**
表示所有以/static/
开头的请求都会被映射到类路径下的/static/
目录中查找静态资源。
3. 使用springmvc
的<mvc:resources>
标签
在传统的Spring MVC项目中,可以在spring-servlet.xml
配置文件中使用<mvc:resources>
标签来配置静态资源。例如:
xml复制
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 静态资源访问配置 -->
<mvc:resources mapping="/static/**" location="/static/" />
<!-- 启用Spring MVC的注解支持 -->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
在这个配置中,/static/**
表示所有以/static/
开头的请求都会被映射到Web应用的/static/
目录中查找静态资源。
4. 使用Spring Boot的application.properties
或application.yml
文件
在Spring Boot项目中,可以在application.properties
或application.yml
文件中配置静态资源的访问路径。例如:
application.properties:
properties复制
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/
application.yml:
yaml复制
spring:
mvc:
static-path-pattern: /static/**
resources:
static-locations: classpath:/static/