Skip to content

Spring MVC

MVC 架构

┌─────────────────────────────────────────────────────────────────┐
│                       Spring MVC 请求处理流程                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   请求 ──→ DispatcherServlet ──→ HandlerMapping                  │
│                              ↓                                   │
│                        HandlerAdapter                            │
│                              ↓                                   │
│                         Controller                               │
│                              ↓                                   │
│                         ViewResolver                             │
│                              ↓                                   │
│                            View                                  │
│                              ↓                                   │
│                           响应                                   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

@Controller 与 @RestController

java
// 普通 Controller(返回视图)
@Controller
public class UserController {

    @RequestMapping("/user")
    public String getUser(Model model) {
        model.addAttribute("user", userService.getUser());
        return "userDetail";  // 视图名
    }
}

// REST Controller(返回数据)
@RestController
@RequestMapping("/api/users")
public class UserApiController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    // @RestController = @Controller + @ResponseBody
    // 所有方法自动返回 JSON
}

请求映射

@RequestMapping

java
@RestController
@RequestMapping("/api/users")
public class UserController {

    // GET 请求
    @GetMapping
    public List<User> listUsers() { ... }

    // POST 请求
    @PostMapping
    public User createUser(@RequestBody User user) { ... }

    // PUT 请求
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) { ... }

    // DELETE 请求
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) { ... }

    // 组合注解
    @GetMapping("/{id}")
    @PostMapping("/search")
}

请求参数绑定

java
@GetMapping("/user")
public User getUser(
    @PathVariable Long id,           // 路径变量
    @RequestParam String name,       // 请求参数
    @RequestParam(defaultValue = "10") int pageSize,  // 默认值
    @RequestParam(required = false) String email,      // 可选
    @RequestHeader String authorization,  // 请求头
    @CookieValue("JSESSIONID") String sessionId  // Cookie
) { ... }

// 实体类绑定
@PostMapping("/user")
public User createUser(@RequestBody User user) { ... }

数据绑定

简单类型绑定

java
// String -> int
@GetMapping("/user/{id}")
public User getUser(@PathVariable("id") Long id) { ... }

// 日期格式化
@InitBinder
protected void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Date.class,
        new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
}

复杂对象绑定

java
// 嵌套对象
public class CreateUserRequest {
    private String name;
    private Integer age;
    private Address address;  // 嵌套对象

    // getters and setters
}

public class Address {
    private String city;
    private String street;

    // getters and setters
}

// HTTP 请求
// name=zhang&age=25&address.city=beijing&address.street=chaoyang

数组和集合绑定

java
// 数组参数
@GetMapping("/search")
public List<User> search(
    @RequestParam String[] interests
) { ... }
// URL: /search?interests=java&interests=spring

// List 参数
@GetMapping("/search")
public List<User> search(
    @RequestParam List<String> interests
) { ... }

数据校验

@Valid 与 @Validated

java
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {

    @PostMapping
    public User createUser(@Valid @RequestBody UserRequest request) {
        // 校验失败会抛出 MethodArgumentNotValidException
        return userService.createUser(request);
    }
}

public class UserRequest {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度3-20")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄最小0")
    @Max(value = 150, message = "年龄最大150")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
}

校验注解

注解说明
@NotNull不能为 null
@NotBlank不能为空字符串
@NotEmpty不能为空(集合/字符串)
@Size长度范围
@Min/@Max数值范围
@Email邮箱格式
@Pattern正则表达式
@Past过去的时间
@Future未来的时间

异常处理

@ExceptionHandler

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessException(BusinessException e) {
        return Result.error(e.getCode(), e.getMessage());
    }

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.error(400, message);
    }

    // 处理通用异常
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        return Result.error(500, "系统异常");
    }
}

@ControllerAdvice

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 绑定模型属性,全局共享
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("version", "1.0");
    }

    // 统一编码
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.setMessageCodesResolver(new DefaultMessageCodesResolver());
    }
}

拦截器

HandlerInterceptor

java
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler) throws Exception {
        // 请求处理之前被调用
        String token = request.getHeader("Authorization");
        if (token == null || !tokenService.validate(token)) {
            response.setStatus(401);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler,
                          ModelAndView modelAndView) {
        // 请求处理之后,视图渲染之前
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                               HttpServletResponse response,
                               Object handler,
                               Exception ex) {
        // 整个请求处理完毕(视图渲染之后)
    }
}

注册拦截器

java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
            .addPathPatterns("/api/**")      // 拦截路径
            .excludePathPatterns("/api/login");  // 排除路径
    }
}

静态资源

java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 方式1:默认静态资源位置
        // classpath:/static/
        // classpath:/public/
        // classpath:/resources/

        // 方式2:自定义静态资源路径
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/");

        // 方式3:外部目录
        registry.addResourceHandler("/files/**")
            .addResourceLocations("file:D:/uploads/");
    }
}

CORS 跨域

@CrossOrigin

java
// 方法级
@RestController
@RequestMapping("/api")
public class UserController {

    @CrossOrigin(origins = "http://example.com")
    @GetMapping("/user")
    public User getUser() { ... }
}

// 类级
@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/api")
public class UserController { }

全局配置

java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://example.com", "http://example2.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("*")
            .allowCredentials(true)
            .maxAge(3600);
    }
}

文件上传

java
@RestController
public class FileController {

    @PostMapping("/upload")
    public Result upload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return Result.error("文件不能为空");
        }

        String filename = file.getOriginalFilename();
        String path = "D:/uploads/" + filename;

        try {
            file.transferTo(new File(path));
            return Result.success("/uploads/" + filename);
        } catch (IOException e) {
            return Result.error("上传失败");
        }
    }
}
yaml
# application.yml
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB
      max-request-size: 100MB

总结

Spring MVC 核心知识点

知识点面试频率实战重要性
请求处理流程⭐⭐⭐⭐⭐⭐⭐⭐⭐
@RequestMapping⭐⭐⭐⭐⭐⭐⭐⭐⭐
参数绑定⭐⭐⭐⭐⭐⭐⭐⭐⭐
数据校验⭐⭐⭐⭐⭐⭐⭐⭐
异常处理⭐⭐⭐⭐⭐⭐⭐⭐
拦截器⭐⭐⭐⭐⭐⭐

⚠️ 易错点提醒

  1. @RestController = @Controller + @ResponseBody
  2. @PathVariable 获取路径参数,@RequestParam 获取查询参数
  3. @Valid 和 @Validated 触发校验
  4. @ExceptionHandler 只处理被 @Controller 抛出的异常
  5. 文件上传要配置 multipart,否则 MultipartFile 为空