Spring MVC 注释新手教程

这是一份非常全面的 Spring MVC 注解全教程,从基础到高级,涵盖了几乎所有常用注解。

1. Spring MVC 基础概念

Spring MVC 工作原理简图:

  1. 用户请求到达 DispatcherServlet(前端控制器)

  2. DispatcherServlet 查询 HandlerMapping 找到对应的控制器

  3. 控制器处理请求并返回逻辑视图名或数据

  4. DispatcherServlet 查询 ViewResolver 解析视图

  5. 渲染视图并返回响应


2. 控制器相关注解

@Controller

  • 作用:标记一个类作为Spring MVC控制器

  • 说明:配合视图解析器使用,方法通常返回视图名

java 复制代码
@Controller
public class HomeController {
    
    @GetMapping("/home")
    public String home() {
        return "home"; // 解析为 /WEB-INF/views/home.jsp
    }
}

@RestController

  • 作用 :组合注解 = @Controller + @ResponseBody

  • 说明:适用于RESTful API,方法返回值直接写入响应体

java 复制代码
@RestController
public class ApiController {
    
    @GetMapping("/api/data")
    public User getData() {
        return new User("John", 25); // 直接返回JSON
    }
}

@RequestMapping

  • 作用:映射HTTP请求到控制器方法
java 复制代码
@Controller
@RequestMapping("/users") // 类级别路径
public class UserController {
    
    // 访问路径: /users/profile
    @RequestMapping("/profile")
    public String profile() {
        return "profile";
    }
    
    // 指定方法和更多属性
    @RequestMapping(value = "/save", method = {RequestMethod.POST, RequestMethod.PUT})
    public String saveUser() {
        return "success";
    }
}

3. 请求映射注解(@RequestMapping 的快捷方式)

注解 说明 示例
@GetMapping 处理GET请求 @GetMapping("/users")
@PostMapping 处理POST请求 @PostMapping("/users")
@PutMapping 处理PUT请求 @PutMapping("/users/{id}")
@DeleteMapping 处理DELETE请求 @DeleteMapping("/users/{id}")
@PatchMapping 处理PATCH请求 @PatchMapping("/users/{id}")

完整示例:

java 复制代码
@RestController
@RequestMapping("/api/v1")
public class UserController {
    
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }
    
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    @PutMapping("/users/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        return userService.update(user);
    }
    
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteById(id);
    }
}

4. 请求参数处理注解

@PathVariable - 路径变量

java 复制代码
@GetMapping("/users/{id}/orders/{orderId}")
public String getUserOrder(
        @PathVariable Long id,
        @PathVariable String orderId,
        @PathVariable Map<String, String> pathVars) { // 获取所有路径变量
    
    System.out.println("用户ID: " + id);           // 123
    System.out.println("订单ID: " + orderId);      // "abc"
    System.out.println("所有路径变量: " + pathVars); // {id=123, orderId=abc}
    return "order-details";
}
// 访问: GET /users/123/orders/abc

@RequestParam - 查询参数

java 复制代码
@GetMapping("/search")
public String searchUsers(
        // 必需参数
        @RequestParam String keyword,
        // 可选参数,带默认值
        @RequestParam(defaultValue = "1") int page,
        @RequestParam(defaultValue = "10") int size,
        // 明确指定非必需
        @RequestParam(required = false) String sort,
        // 获取所有查询参数
        @RequestParam Map<String, String> allParams) {
    
    System.out.println("关键词: " + keyword); // "john"
    System.out.println("页码: " + page);     // 1
    System.out.println("排序: " + sort);     // null
    return "search-results";
}
// 访问: GET /search?keyword=john&page=1

@RequestBody - 请求体

java 复制代码
@PostMapping("/users")
public User createUser(@RequestBody User user) {
    // 自动将JSON请求体转换为User对象
    return userService.save(user);
}

@PostMapping("/batch")
public List<User> createUsers(@RequestBody List<User> users) {
    // 支持集合类型
    return userService.saveAll(users);
}

@RequestHeader - 请求头

java 复制代码
@GetMapping("/info")
public String getUserInfo(
        @RequestHeader("User-Agent") String userAgent,
        @RequestHeader(value = "Authorization", required = false) String auth,
        @RequestHeader Map<String, String> headers) { // 所有请求头
    
    System.out.println("浏览器: " + userAgent);
    System.out.println("Token: " + auth);
    return "user-info";
}

@CookieValue - Cookie值

java 复制代码
@GetMapping("/profile")
public String getProfile(@CookieValue("sessionId") String sessionId,
                        @CookieValue(value = "theme", defaultValue = "light") String theme) {
    System.out.println("会话ID: " + sessionId);
    System.out.println("主题: " + theme);
    return "profile";
}

@RequestPart - 文件上传

java 复制代码
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFile(
        @RequestPart("file") MultipartFile file,
        @RequestPart("metadata") String metadata) { // 其他表单字段
    
    if (!file.isEmpty()) {
        String fileName = file.getOriginalFilename();
        long size = file.getSize();
        // 保存文件...
    }
    return "upload-success";
}

@ModelAttribute - 模型属性

java 复制代码
// 在方法上使用 - 在每个请求前执行,添加公共数据
@ModelAttribute
public void addCommonAttributes(Model model) {
    model.addAttribute("currentYear", LocalDate.now().getYear());
    model.addAttribute("siteName", "我的网站");
}

// 在参数上使用 - 绑定表单数据
@PostMapping("/register")
public String registerUser(@ModelAttribute User user, BindingResult result) {
    if (result.hasErrors()) {
        return "register-form";
    }
    userService.save(user);
    return "redirect:/success";
}

5. 响应处理注解

@ResponseBody - 直接写响应体

java 复制代码
@Controller
public class DataController {
    
    @GetMapping("/user.json")
    @ResponseBody // 返回值直接写入响应体,不经过视图解析器
    public User getUser() {
        return new User("John", 25);
    }
}

@ResponseStatus - 定义HTTP状态码

java 复制代码
@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED) // 201 Created
public User createUser(@RequestBody User user) {
    return userService.save(user);
}

@GetMapping("/not-found")
@ResponseStatus(HttpStatus.NOT_FOUND) // 404 Not Found
public void notFound() {
    // 方法体可以为空,直接返回404
}

@CrossOrigin - 跨域请求

java 复制代码
@RestController
@CrossOrigin(origins = "*", maxAge = 3600) // 类级别配置
public class ApiController {
    
    @GetMapping("/public/data")
    @CrossOrigin(origins = "https://example.com") // 方法级别覆盖
    public String getPublicData() {
        return "public data";
    }
}

6. 数据验证注解

在实体类中定义验证规则:

java 复制代码
public class User {
    private Long id;
    
    @NotBlank(message = "姓名不能为空")
    @Size(min = 2, max = 30, message = "姓名长度必须在2-30之间")
    private String name;
    
    @Email(message = "邮箱格式不正确")
    @NotBlank(message = "邮箱不能为空")
    private String email;
    
    @Min(value = 18, message = "年龄必须大于18岁")
    @Max(value = 100, message = "年龄必须小于100岁")
    private Integer age;
    
    @Pattern(regexp = "^(USER|ADMIN)$", message = "角色必须是USER或ADMIN")
    private String role;
    
    @Future(message = "生效时间必须是未来时间")
    private LocalDateTime effectiveDate;
    
    // getter/setter省略
}

在控制器中进行验证:

java 复制代码
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody User user, 
                                   BindingResult bindingResult) {
    
    if (bindingResult.hasErrors()) {
        // 处理验证错误
        List<String> errors = bindingResult.getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList());
        return ResponseEntity.badRequest().body(errors);
    }
    
    User savedUser = userService.save(user);
    return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}

7. 异常处理注解

@ControllerAdvice + @ExceptionHandler

java 复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    
    // 处理特定异常
    @ExceptionHandler(UserNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
        return new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
    }
    
    // 处理验证异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse handleValidationErrors(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList());
        return new ErrorResponse("VALIDATION_ERROR", errors);
    }
    
    // 处理所有未捕获异常
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ErrorResponse handleAllExceptions(Exception ex) {
        return new ErrorResponse("INTERNAL_ERROR", "系统内部错误");
    }
}

// 错误响应类
public class ErrorResponse {
    private String code;
    private Object message;
    private long timestamp;
    
    public ErrorResponse(String code, Object message) {
        this.code = code;
        this.message = message;
        this.timestamp = System.currentTimeMillis();
    }
    
    // getter/setter
}

8. 模型和视图注解

@SessionAttributes - 会话属性

java 复制代码
@Controller
@RequestMapping("/cart")
@SessionAttributes("cart") // 将名为"cart"的模型属性存入会话
public class ShoppingCartController {
    
    // 初始化购物车
    @ModelAttribute("cart")
    public ShoppingCart initializeCart() {
        return new ShoppingCart();
    }
    
    @PostMapping("/add")
    public String addToCart(@ModelAttribute("cart") ShoppingCart cart,
                           @RequestParam Long productId) {
        cart.addItem(productId);
        return "redirect:/cart/view";
    }
}

直接使用 Model 和 ModelMap

java 复制代码
@Controller
public class PageController {
    
    @GetMapping("/home")
    public String homePage(Model model) {
        // 添加单个属性
        model.addAttribute("welcomeMessage", "欢迎访问我们的网站!");
        
        // 添加多个属性
        Map<String, Object> stats = new HashMap<>();
        stats.put("userCount", 1000);
        stats.put("onlineUsers", 150);
        model.addAllAttributes(stats);
        
        // 添加对象
        model.addAttribute("currentUser", new User("John", 25));
        
        return "home";
    }
    
    @GetMapping("/profile")
    public String profilePage(ModelMap modelMap) {
        // ModelMap 用法类似 Model
        modelMap.addAttribute("pageTitle", "用户资料");
        modelMap.addAttribute("user", userService.getCurrentUser());
        return "profile";
    }
}

9. 配置相关注解

@EnableWebMvc - 启用MVC配置

java 复制代码
@Configuration
@EnableWebMvc // 启用Spring MVC,提供默认配置
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
    
    // 配置视图解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }
    
    // 配置静态资源
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/static/");
    }
}

10. 完整综合示例

java 复制代码
@RestController
@RequestMapping("/api/v1/products")
@Validated
@CrossOrigin(origins = "*")
public class ProductController {
    
    @GetMapping
    public Page<Product> getProducts(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(required = false) String category,
            @RequestParam(required = false) @Min(0) Double minPrice,
            @RequestParam(required = false) @Max(10000) Double maxPrice) {
        
        return productService.findProducts(page, size, category, minPrice, maxPrice);
    }
    
    @GetMapping("/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productService.findById(id)
                .orElseThrow(() -> new ProductNotFoundException("产品不存在: " + id));
    }
    
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Product createProduct(@Valid @RequestBody Product product) {
        return productService.save(product);
    }
    
    @PutMapping("/{id}")
    public Product updateProduct(@PathVariable Long id, 
                                @Valid @RequestBody Product product) {
        product.setId(id);
        return productService.update(product);
    }
    
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteProduct(@PathVariable Long id) {
        productService.deleteById(id);
    }
    
    @PostMapping("/{id}/images")
    public Product uploadImage(@PathVariable Long id,
                              @RequestPart("image") MultipartFile image) {
        return productService.uploadImage(id, image);
    }
}

这份教程涵盖了 Spring MVC 中最重要和最常用的注解,掌握这些注解就能够应对大多数的 Web 开发场景。建议在实际项目中多加练习,熟能生巧!

相关推荐
小羊没烦恼!3 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里3 天前
java中的继承和多态的区别
java
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕3 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖3 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时3 天前
01-06-A-JVM排查实战详解
java·jvm
vipxieliang3 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
ba_pi3 天前
mysql 查询所有表名并授权
java
ProcessOn官方账号3 天前
java函数式编程--入门基础
java·编程·函数式编程