Spring MVC 框架

主要作用

Spring MVC 是一个基于 Java 的 Web 框架,主要用于构建 Web 应用程序。它的主要作用是将用户的 HTTP 请求与业务逻辑进行解耦,通过 MVC(模型 - 视图 - 控制器)模式来组织代码,使得代码结构更加清晰,便于维护和扩展。它能够处理用户的请求,调用业务逻辑层的方法,并将处理结果通过视图展示给用户。

核心组件

  1. DispatcherServlet
    • 作为前端控制器,DispatcherServlet 是 Spring MVC 的核心。它接收所有的 HTTP 请求,并根据请求信息(如 URL、HTTP 方法等)来决定由哪一个处理器(Controller)来处理请求。它就像一个交通指挥官,负责将请求分发到合适的处理器。
  2. HandlerMapping
    • 它的作用是将请求映射到处理器(Controller)。DispatcherServlet 在接收到请求后,会通过 HandlerMapping 来查找能够处理该请求的处理器。HandlerMapping 会根据请求的 URL、请求方法等因素来确定最合适的处理器。
  3. Controller(处理器)
    • 它是业务逻辑的实现部分。Controller 接收 DispatcherServlet 分发的请求,执行业务逻辑处理,然后返回一个 ModelAndView 对象。ModelAndView 对象包含了模型数据和视图信息。
  4. ViewResolver(视图解析器)
    • 它的作用是根据逻辑视图名解析成真正的视图(View),比如将视图名称解析为一个 JSP 页面路径。当 Controller 处理完请求后,会返回一个逻辑视图名,ViewResolver 会根据这个逻辑视图名找到对应的物理视图资源(如 JSP 文件)。

调用流程

  1. 用户发送请求到服务器,请求首先到达 DispatcherServlet
  2. DispatcherServlet 通过 HandlerMapping 查找能够处理该请求的处理器(Controller)。
  3. HandlerMapping 找到对应的处理器后,DispatcherServlet 将请求转发给该处理器。
  4. 处理器(Controller)执行业务逻辑处理,处理完成后返回一个 ModelAndView 对象给 DispatcherServlet
  5. DispatcherServlet 接收到 ModelAndView 对象后,根据其中的逻辑视图名,通过 ViewResolver 解析出真正的视图(如 JSP 页面)。
  6. 视图(如 JSP 页面)会根据 ModelAndView 对象中的模型数据进行渲染,生成最终的 HTML 页面。
  7. 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 提供了多种方式来接收请求参数:

请求头接收

可以通过 @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),并使用模型数据渲染页面。

转发和重定向

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 方法表示删除资源。

具体规范

请求方式和请求参数选择

功能扩展

全局异常处理

可以通过 @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

用途

示例

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

用途

示例

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

用途

示例

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注解。

总结

这些注解在处理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.propertiesapplication.yml文件

在Spring Boot项目中,可以在application.propertiesapplication.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/