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";
    }
}

直接使用 ModelModelMap

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 开发场景。建议在实际项目中多加练习,熟能生巧!

相关推荐
moxiaoran57533 小时前
PocketBase轻量级后端解决方案
java·pocketbase
陈果然DeepVersion3 小时前
Java大厂面试真题:Spring Boot+Kafka+AI智能客服场景全流程解析(七)
java·人工智能·spring boot·微服务·kafka·面试题·rag
4Forsee3 小时前
【Android】消息机制
android·java·前端
骚戴3 小时前
PDF或Word转图片(多线程+aspose+函数式接口)
java·开发语言
姓蔡小朋友3 小时前
SpringDataRedis
java·开发语言·redis
CodeCraft Studio3 小时前
国产化Excel处理控件Spire.XLS教程:如何使用 Java 将 TXT 文本转换为 Excel 表格
java·word·excel·spire·文档格式转换·txt转excel
朝新_4 小时前
【SpringBoot】玩转 Spring Boot 日志:级别划分、持久化、格式配置及 Lombok 简化使用
java·spring boot·笔记·后端·spring·javaee
m0_748248024 小时前
Spring设计模式刨根问底
java·spring·设计模式
喝杯牛奶丶4 小时前
MySQL隔离级别:大厂为何偏爱RC?
java·数据库·mysql·面试