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

相关推荐
蛋蛋的就会顺顺的33 分钟前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
需要8261 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
小 黄 鸡1 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
伯恩bourne3 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
silvamux_3 小时前
curl 测试 OpenAI 兼容 API 的完整流程
java·人工智能·缓存·ai·oneapi
省钱兄--zs4 小时前
深圳24小时自助健身房软硬件解决方案实战指南与开发流程详解
java·数据仓库·spring boot·小程序·架构
顽疲4 小时前
Java vue 养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践
java·开发语言·vue.js·vue养老·springboot养老·java养老
Wang's Blog4 小时前
Java 项目实战: 交友项目 - 安装开发工具与环境准备
java·开发语言·交友
用户21296835261685 小时前
开源了一个 Java 多智能体 LLM 框架 Baton:一个依赖,把 Agent 循环、工具调用、记忆、编排全配齐
java·llm
helmsail5 小时前
05 管理活动与选品
java