Skip to content

Spring MVC 简介

什么是Spring MVC?

Spring MVC是Spring Framework的一部分,是一个基于Java的Web MVC框架。它采用Model-View-Controller设计模式,帮助开发者构建灵活、松耦合的Web应用程序。

MVC架构

Model(模型)

负责业务逻辑和数据处理。

View(视图)

负责展示数据给用户。

Controller(控制器)

负责接收用户请求,调用模型处理业务,选择视图展示结果。

Client → Controller → Service → Repository → Database

         View → Client

Spring MVC核心组件

1. DispatcherServlet

前端控制器,负责请求分发。

java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    // 配置
}

2. Controller

处理HTTP请求。

java
@Controller
@RequestMapping("/users")
public class UserController {
    
    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id, Model model) {
        User user = userService.getUserById(id);
        model.addAttribute("user", user);
        return "user-detail";
    }
}

3. HandlerMapping

将请求映射到处理器。

4. HandlerAdapter

执行处理器。

5. ViewResolver

解析视图名称。

java
@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
}

@Controller vs @RestController

@Controller

返回视图名称。

java
@Controller
public class PageController {
    
    @GetMapping("/home")
    public String home(Model model) {
        model.addAttribute("message", "Welcome");
        return "home"; // 返回视图名称
    }
}

@RestController

直接返回数据(JSON/XML),相当于@Controller + @ResponseBody。

java
@RestController
@RequestMapping("/api/users")
public class UserRestController {
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id); // 自动转换为JSON
    }
}

请求映射

@RequestMapping

java
@Controller
@RequestMapping("/products")
public class ProductController {
    
    // GET /products
    @RequestMapping(method = RequestMethod.GET)
    public String list() {
        return "product-list";
    }
    
    // POST /products
    @RequestMapping(method = RequestMethod.POST)
    public String create(@ModelAttribute Product product) {
        productService.save(product);
        return "redirect:/products";
    }
}

简化注解

java
@RestController
@RequestMapping("/api/products")
public class ProductApiController {
    
    @GetMapping // GET请求
    public List<Product> list() {
        return productService.findAll();
    }
    
    @PostMapping // POST请求
    public Product create(@RequestBody Product product) {
        return productService.save(product);
    }
    
    @PutMapping("/{id}") // PUT请求
    public Product update(@PathVariable Long id, @RequestBody Product product) {
        return productService.update(id, product);
    }
    
    @DeleteMapping("/{id}") // DELETE请求
    public void delete(@PathVariable Long id) {
        productService.delete(id);
    }
    
    @PatchMapping("/{id}") // PATCH请求
    public Product partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
        return productService.partialUpdate(id, updates);
    }
}

参数绑定

1. @PathVariable - 路径变量

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

// 多个路径变量
@GetMapping("/users/{userId}/orders/{orderId}")
public Order getUserOrder(@PathVariable Long userId, @PathVariable Long orderId) {
    return orderService.getOrder(userId, orderId);
}

// 自定义变量名
@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long userId) {
    return userService.getUserById(userId);
}

2. @RequestParam - 查询参数

java
// GET /search?keyword=spring&page=1&size=10
@GetMapping("/search")
public List<Product> search(
    @RequestParam String keyword,
    @RequestParam(defaultValue = "1") int page,
    @RequestParam(defaultValue = "10") int size) {
    return productService.search(keyword, page, size);
}

// 可选参数
@GetMapping("/users")
public List<User> getUsers(@RequestParam(required = false) String name) {
    if (name != null) {
        return userService.findByName(name);
    }
    return userService.findAll();
}

3. @RequestBody - 请求体

java
@PostMapping("/users")
public User createUser(@RequestBody User user) {
    return userService.save(user);
}

// 配合@Valid进行校验
@PostMapping("/users")
public User createUser(@Valid @RequestBody User user) {
    return userService.save(user);
}

4. @RequestHeader - 请求头

java
@GetMapping("/info")
public String getInfo(
    @RequestHeader("User-Agent") String userAgent,
    @RequestHeader(value = "Authorization", required = false) String token) {
    return "User-Agent: " + userAgent;
}

5. @CookieValue - Cookie值

java
@GetMapping("/preference")
public String getPreference(@CookieValue(value = "theme", defaultValue = "light") String theme) {
    return "Current theme: " + theme;
}

6. @ModelAttribute - 表单绑定

java
@PostMapping("/register")
public String register(@ModelAttribute User user) {
    userService.register(user);
    return "redirect:/login";
}

7. @SessionAttribute - 会话属性

java
@GetMapping("/profile")
public String profile(@SessionAttribute("currentUser") User user, Model model) {
    model.addAttribute("user", user);
    return "profile";
}

返回类型

1. String - 视图名称

java
@GetMapping("/home")
public String home() {
    return "home"; // 返回home.jsp
}

2. ModelAndView

java
@GetMapping("/user/{id}")
public ModelAndView getUser(@PathVariable Long id) {
    ModelAndView mav = new ModelAndView("user-detail");
    User user = userService.getUserById(id);
    mav.addObject("user", user);
    return mav;
}

3. ResponseEntity

java
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    User user = userService.getUserById(id);
    if (user == null) {
        return ResponseEntity.notFound().build();
    }
    return ResponseEntity.ok(user);
}

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
    User savedUser = userService.save(user);
    return ResponseEntity
        .created(URI.create("/api/users/" + savedUser.getId()))
        .body(savedUser);
}

4. void - 异步处理

java
@GetMapping("/async")
public void asyncProcess(HttpServletResponse response) throws IOException {
    // 异步处理
    response.getWriter().write("Processing...");
}

5. 对象 - 自动转JSON

java
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    return userService.getUserById(id); // 自动转换为JSON
}

完整CRUD示例

java
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // 查询所有用户
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
        Page<User> userPage = userService.findAll(PageRequest.of(page, size));
        return ResponseEntity.ok(userPage.getContent());
    }
    
    // 根据ID查询用户
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.getUserById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }
    
    // 创建用户
    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        User savedUser = userService.save(user);
        return ResponseEntity
            .created(URI.create("/api/users/" + savedUser.getId()))
            .body(savedUser);
    }
    
    // 更新用户
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(
        @PathVariable Long id,
        @Valid @RequestBody User user) {
        return userService.update(id, user)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }
    
    // 删除用户
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        if (userService.delete(id)) {
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }
}

异常处理

局部异常处理

java
@RestController
public class UserController {
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
        return ResponseEntity
            .status(HttpStatus.NOT_FOUND)
            .body(ex.getMessage());
    }
}

全局异常处理

java
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(404, ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
        ErrorResponse error = new ErrorResponse(500, "内部服务器错误");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

拦截器

java
public class LoggingInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) {
        System.out.println("请求URL: " + request.getRequestURI());
        return true; // 返回true继续处理,false中断
    }
    
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView modelAndView) {
        System.out.println("请求处理完成");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) {
        System.out.println("视图渲染完成");
    }
}

// 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggingInterceptor())
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/public/**");
    }
}

跨域配置

java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

// 或使用注解
@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class UserController {
    // ...
}

下一步