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

相关推荐
tb_first12 小时前
SSM速通4
java·jvm·spring·tomcat·maven·mybatis
百炼成神 LV@菜哥12 小时前
Kylin Linux V10 aarch64安装DBeaver
java·linux·服务器·kylin
有代理ip12 小时前
成功请求的密码:HTTP 2 开头响应码深度解析
java·大数据·python·算法·php
好好沉淀13 小时前
ES 脚本核心语法:ctx._source [‘group_id‘]
java·elasticsearch·script
李慕婉学姐13 小时前
【开题答辩过程】以《基于Spring Boot的疗养院理疗管理系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·后端
tb_first13 小时前
SSM速通2
java·javascript·后端
qq_124987075313 小时前
基于协同过滤算法的运动场馆服务平台设计与实现(源码+论文+部署+安装)
java·大数据·数据库·人工智能·spring boot·毕业设计·计算机毕业设计
大飞哥~BigFei13 小时前
自定义注解记录接口切面log日志入库优化
java
人道领域13 小时前
javaWeb从入门到进阶(maven高级进阶)
java·spring·maven
一路向北⁢13 小时前
Spring Boot 3 整合 SSE (Server-Sent Events) 企业级最佳实践(一)
java·spring boot·后端·sse·通信