SpringMVC常用注解

目录

一、简介

SpringMVC的注解有很多,常用的注解有下面这几个:

1、为什么要使用注解

使用注解可以简化配置,提高代码的可读性和可维护性。通过注解可以实现依赖注入,减少手动管理对象的代码量。注解还支持面向切面编程,实现切面、切入点和通知等。此外,注解提供了声明式事务管理的支持,简化了事务配置和管理。注解还可以用于组件扫描和自动装配,提高开发效率。最后,注解在测试时也有很好的支持。总之,注解使得代码更简洁、灵活,并能更好地利用框架的功能和特性。

代码简洁:减少XML配置,代码更易读

开发快速:自动完成常见任务,减少样板代码

类型安全:编译时检查,减少运行时错误

灵活强大:支持各种Web开发需求

易于测试:注解使单元测试更简单

现代化:符合现代Java开发最佳实践

一、@Controller注解和@RestController注解

1、@Controller注解

@Controller 是Spring MVC中用于标记类作为控制器的注解,主要用于传统的Web页面应用

java 复制代码
@Controller
@RequestMapping("/web")
public class UserWebController {
    
    @Autowired
    private UserService userService;
    
    // 返回视图名称,由视图解析器解析为具体页面
    @GetMapping("/users")
    public String getUserList(Model model) {
        List<User> users = userService.findAll();
        model.addAttribute("users", users);
        return "user-list"; // 返回视图名,对应 user-list.jsp/html
    }
    
    // 返回JSON数据,需要额外添加@ResponseBody
    @GetMapping("/users/json")
    @ResponseBody
    public List<User> getUsersJson() {
        return userService.findAll();
    }
    
    // 处理表单提交
    @PostMapping("/users")
    public String createUser(@ModelAttribute User user) {
        userService.save(user);
        return "redirect:/web/users"; // 重定向
    }
    
    // 显示用户详情页面
    @GetMapping("/users/{id}")
    public String getUserDetail(@PathVariable Long id, Model model) {
        User user = userService.findById(id);
        model.addAttribute("user", user);
        return "user-detail";
    }
}

根据上面的例子可以看出来:

@Controller 主要用途是返回页面(视图)

处理 ModelAndView 中的 View 部分
返回视图:默认返回视图名称,由视图解析器渲染HTML页面

需要@ResponseBody:如果要返回JSON/XML数据,需要额外添加@ResponseBody

适合:传统Web应用,需要服务端渲染页面的场景

2、@RestController注解

@RestController 是Spring 4.0引入的注解,是@Controller@ResponseBody的组合,专门用于RESTful Web服务。

@RestController是前后端分离开发主要的控制器注解。

java 复制代码
@RestController
@RequestMapping("/api/v1")
public class UserApiController {
    
    @Autowired
    private UserService userService;
    
    // 自动返回JSON数据,不需要@ResponseBody
    @GetMapping("/users")
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    
    @PostMapping("/users")
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        User savedUser = userService.save(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
    }
    
    @PutMapping("/users/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, 
                                         @Valid @RequestBody User user) {
        User updatedUser = userService.update(id, user);
        return ResponseEntity.ok(updatedUser);
    }
    
    @DeleteMapping("/users/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
    
    // 返回自定义响应结构
    @GetMapping("/users/{id}/profile")
    public ApiResponse<UserProfile> getUserProfile(@PathVariable Long id) {
        UserProfile profile = userService.getProfile(id);
        return ApiResponse.success(profile);
    }
}

自动@ResponseBody:所有方法自动添加@ResponseBody,直接返回数据

返回JSON/XML:默认返回数据而不是视图名称

适合:REST API、前后端分离应用

@Controller和@RestController注解的区别如下图:

二、@RequestParam注解

@RequestParam 用于从HTTP请求的查询参数或表单数据 中提取值到控制器方法的参数中。
@RequestParam注解是用来解析下面的情况的:/users?page=1&name=john
@RequestParam注解能使用在所有的请求方法中包括(GET、POST、PUT、DELETE)等,但是只能解析HTTP请求的查询参数或表单数据,不能解析请求体。

java 复制代码
@RestController
public class UserController {
    
    // 基本用法:从URL参数获取值
    // 访问:/search?keyword=java
    @GetMapping("/search")
    public String search(@RequestParam String keyword) {
        return "Searching for: " + keyword;
    }
    
    // 多个参数
    // 访问:/users?page=1&size=20
    @GetMapping("/users")
    public List<User> getUsers(
        @RequestParam int page, 
        @RequestParam int size) {
        return userService.findUsers(page, size);
    }
}

请求参数为一个参数的情况下,可以忽略使用@RequestParam注解,但是请求参数为多个的时候必须使用@RequestParam注解

1、注解属性详解

1、name / value 属性

java 复制代码
@RestController
public class ExampleController {
    
    // 参数名与方法参数名不同时使用
    // 访问:/api?user_name=john
    @GetMapping("/api")
    public String example(
        @RequestParam(name = "user_name") String username,
        @RequestParam(value = "user_age") Integer age) {
        return "Username: " + username + ", Age: " + age;
    }
    
    // name和value是等价的
    @GetMapping("/test")
    public String test(
        @RequestParam("first_name") String firstName,  // 使用value
        @RequestParam(name = "last_name") String lastName) { // 使用name
        return firstName + " " + lastName;
    }
}

name 和 value 属性是等价的 ,用于指定请求参数的名称,当方法参数名与URL参数名不一致时使用。
场景1:参数名与方法参数名一致(不需要指定)

java 复制代码
@RestController
public class SimpleController {
    
    // 方法参数名与URL参数名一致时,不需要指定name/value
    // 访问:/user?userId=123&username=john
    @GetMapping("/user")
    public User getUser(
        @RequestParam Long userId,       // 自动匹配 userId 参数
        @RequestParam String username    // 自动匹配 username 参数
    ) {
        return userService.findUser(userId, username);
    }
}

场景2:参数名与方法参数名不一致(需要指定name/value)

java 复制代码
@RestController
public class ComplexController {
    
    // 方法参数名与URL参数名不一致时,需要使用name/value指定
    // 访问:/search?q=springboot&cat=technology
    @GetMapping("/search")
    public List<Item> searchItems(
        @RequestParam("q") String keyword,           // URL参数是q,方法参数是keyword
        @RequestParam(name = "cat") String category, // URL参数是cat,方法参数是category
        @RequestParam(value = "p") int page          // URL参数是p,方法参数是page
    ) {
        return searchService.search(keyword, category, page);
    }
}

2、required 属性

@RequestParam 注解主要用于从请求 URL 的查询参数或表单数据中提取值。

required 属性用于控制该参数是否必须由客户端提供,required属性的默认值为tue,即是必须传的。

重要:当你使用 required = false 时,对应的方法参数类型必须是能够接受 null 值的,例如:

所有引用类型(如 String, Integer, Long, 自定义对象等)。

如果使用基本数据类型(如 int, long, boolean),因为它们不能为 null,Spring 会尝试绑定一个默认值(如 0 或false),但这通常不是我们想要的行为,容易引发逻辑错误。

请求参数中必须为引用类型数据

java 复制代码
@RestController
public class RequiredExampleController {
    
    // 必需参数(默认true)
    // 访问必须包含id参数:/user?id=123
    @GetMapping("/user")
    public User getUser(@RequestParam Long id) {
        return userService.findById(id);
    }
    
    // 可选参数
    // 访问可以不带sort参数:/products 或 /products?sort=name
    @GetMapping("/products")
    public List<Product> getProducts(
        @RequestParam(required = false) String sort) {
        if (sort != null) {
            return productService.findAllSorted(sort);
        }
        return productService.findAll();
    }
    
    // 混合使用
    @GetMapping("/search")
    public List<Item> searchItems(
        @RequestParam String keyword,           // 必需
        @RequestParam(required = false) String category,  // 可选
        @RequestParam(required = false) Double minPrice) { // 可选
        return itemService.search(keyword, category, minPrice);
    }
}

3、defaultValue 属性

defaultValue 属性用于为请求参数指定一个默认值。当客户端没有在请求中提供该参数时,Spring 会自动使用这个默认值。

自动设置 required = false:一旦你设置了 defaultValuerequired 属性就会被自动视为false,即使你显式地写成 required = true 也会被忽略。因为既然有默认值,这个参数就不再是"必需"的了。
类型转换:defaultValue 是一个字符串,但 Spring 会自动将其转换为方法参数的实际类型(如 int, long,boolean 等)。

设置了defaultValue的话,required如果没有被设置的话,就会自动被设置为false的。

java 复制代码
@RestController
public class DefaultValueController {
    
    // 提供默认值
    // 访问 /page 或 /page?num=5 都可以
    @GetMapping("/page")
    public String getPage(
        @RequestParam(defaultValue = "1") int pageNum) {
        return "Page number: " + pageNum;
    }
    
    // 常用分页参数
    @GetMapping("/items")
    public Page<Item> getItems(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "20") int size,
        @RequestParam(defaultValue = "id") String sortBy,
        @RequestParam(defaultValue = "asc") String direction) {
        return itemService.getItems(page, size, sortBy, direction);
    }
    
    // 字符串默认值
    @GetMapping("/filter")
    public List<Product> filterProducts(
        @RequestParam(defaultValue = "") String name,
        @RequestParam(defaultValue = "all") String category,
        @RequestParam(defaultValue = "0") double minPrice) {
        return productService.filter(name, category, minPrice);
    }
}

2、高级用法

特殊情况:什么时候可以省略@RequestParam注解?

只有在 POJO 对象作为参数 时才可以省略注解,Spring 会尝试将请求参数按名称匹配到对象的属性上。

1、映射到对象

java 复制代码
// 搜索参数对象
public class SearchCriteria {
    private String keyword;
    private String category;
    private Double minPrice;
    private Double maxPrice;
    private String sortBy;
    
    // getter和setter
}

@RestController
public class SearchController {
    
    // Spring自动将参数映射到对象属性
    // 访问:/search?keyword=phone&category=electronics&minPrice=100
    @GetMapping("/search")
    public List<Product> searchProducts(SearchCriteria criteria) {
        return productService.search(criteria);
    }
}

2、数组/列表参数

java 复制代码
@RestController
public class ArrayParamController {
    
    // 多选参数
    // 访问:/products?categories=electronics&categories=books
    @GetMapping("/products")
    public List<Product> getByCategories(
        @RequestParam List<String> categories) {
        return productService.findByCategories(categories);
    }
    
    // 数组形式
    // 访问:/users?ids=1,2,3,4 或 /users?ids=1&ids=2&ids=3
    @GetMapping("/users")
    public List<User> getUsersByIds(@RequestParam Long[] ids) {
        return userService.findByIds(ids);
    }
    
    // 整数列表
    @GetMapping("/orders")
    public List<Order> getOrdersByStatus(
        @RequestParam(defaultValue = "1,2,3") List<Integer> statuses) {
        return orderService.findByStatusIn(statuses);
    }
}

3、Map接收所有参数

java 复制代码
@RestController
public class MapParamController {
    
    // 接收所有查询参数到Map中
    @GetMapping("/filter")
    public List<Item> filterItems(
        @RequestParam Map<String, String> allParams) {
        return itemService.filterByMultipleCriteria(allParams);
    }
    
    // 带前缀的参数映射
    @GetMapping("/advanced-search")
    public List<Product> advancedSearch(
        @RequestParam Map<String, Object> searchParams) {
        // 可以处理各种类型的参数值
        return productService.advancedSearch(searchParams);
    }
}

三、@PathVariable注解

1、基础概念

@PathVariable 用于从URL路径模板中提取值到控制器方法的参数中,是RESTful API设计的核心注解。

2、基本语法

1、单个路径变量

java 复制代码
@RestController
@RequestMapping("/api")
public class UserController {
    
    // 访问:GET /api/users/123
    @GetMapping("/users/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    // 访问:GET /api/products/abc-123
    @GetMapping("/products/{productCode}")
    public Product getProduct(@PathVariable String productCode) {
        return productService.findByCode(productCode);
    }
}

2、多个路径变量

java 复制代码
@RestController
public class OrderController {
    
    // 访问:GET /users/123/orders/456
    @GetMapping("/users/{userId}/orders/{orderId}")
    public Order getOrder(
        @PathVariable Long userId,
        @PathVariable Long orderId) {
        return orderService.findUserOrder(userId, orderId);
    }
    
    // 访问:GET /categories/electronics/products/laptop
    @GetMapping("/categories/{categoryName}/products/{productName}")
    public List<Product> getProducts(
        @PathVariable String categoryName,
        @PathVariable String productName) {
        return productService.findByCategoryAndName(categoryName, productName);
    }
}

3、注解属性详解

1、name / value 属性

name 和 value 属性是完全等价的,都用于指定路径变量名

当路径变量名与方法参数名一致时,可以省略

当名称不一致时,必须显式指定

@PathVariable的name/value属性和@RequestParam的name/value属性一样。

java 复制代码
@RestController
public class PathVariableController {
    
    // ✅ 情况1:名称一致,可以省略(最常用)
    // 访问:GET /api/v1/users/123
    @GetMapping("/api/v1/users/{userId}")
    public User getUserV1(@PathVariable Long userId) {
        return userService.findById(userId);
    }
    
    // ✅ 情况2:名称不一致,必须指定(使用value)
    // 访问:GET /api/v2/users/123
    @GetMapping("/api/v2/users/{userId}")
    public User getUserV2(@PathVariable("userId") Long id) {
        return userService.findById(id);
    }
    
    // ✅ 情况3:名称不一致,必须指定(使用name)
    @GetMapping("/api/v3/users/{userId}")
    public User getUserV3(@PathVariable(name = "userId") Long userIdentifier) {
        return userService.findById(userIdentifier);
    }
    
    // ✅ 情况4:显式使用value属性
    @GetMapping("/api/v4/users/{userId}")
    public User getUserV4(@PathVariable(value = "userId") Long uid) {
        return userService.findById(uid);
    }
    
    // ❌ 错误示例:名称不一致且未指定
    @GetMapping("/api/error/users/{userId}")
    public User getErrorUser(@PathVariable Long id) {  // 编译错误!
        return userService.findById(id);
    }
}

2、required 属性详解

与@RequestParam一致。

java 复制代码
@RestController
public class RequiredController {
    
    // ✅ required默认为true - 路径变量必须存在
    // 访问:GET /required/users/123 ✅
    // 访问:GET /required/users/    ❌ 会404(路由不匹配)
    @GetMapping("/required/users/{id}")
    public User getRequiredUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}
java 复制代码
@RestController
public class OptionalPathVariableController {
    
    // ✅ 方案1:通过数组定义多个路由模式
    // 访问:GET /optional/users      ✅ 获取所有用户
    // 访问:GET /optional/users/123  ✅ 获取特定用户
    @GetMapping({"/optional/users", "/optional/users/{id}"})
    public Object getUsers(@PathVariable(required = false) Long id) {
        if (id != null) {
            return userService.findById(id);  // 返回单个用户
        } else {
            return userService.findAll();     // 返回用户列表
        }
    }
    
    // ✅ 方案2:使用Optional包装(Java 8+)
    @GetMapping({"/optional2/users", "/optional2/users/{id}"})
    public Object getUsersOptional(@PathVariable Optional<Long> id) {
        return id.map(userService::findById)
                .orElse(userService.findAll());
    }
    
    // ✅ 方案3:多级可选路径
    @GetMapping({
        "/categories",
        "/categories/{categoryId}", 
        "/categories/{categoryId}/products",
        "/categories/{categoryId}/products/{productId}"
    })
    public Object getHierarchicalData(
        @PathVariable(required = false) Long categoryId,
        @PathVariable(required = false) Long productId) {
        
        if (categoryId == null) {
            return categoryService.findAll();  // 所有分类
        } else if (productId == null) {
            return productService.findByCategory(categoryId);  // 分类下所有商品
        } else {
            return productService.findByIdAndCategory(productId, categoryId);  // 特定商品
        }
    }
}

四、@RequestBody注解

1、基本概念

@RequestBody 用于将 HTTP请求体 中的数据绑定到方法的参数上,主要用于处理JSON、XML等格式的请求数据。

2、基本语法

1、接收简单对象

java 复制代码
@RestController
public class UserController {
    
    // 接收JSON对象
    // 请求体:{"name": "John", "email": "john@example.com", "age": 25}
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    // 接收Map
    // 请求体:{"username": "john", "password": "123456"}
    @PostMapping("/auth/register")
    public ResponseEntity<?> register(@RequestBody Map<String, Object> userData) {
        String username = (String) userData.get("username");
        String password = (String) userData.get("password");
        return ResponseEntity.ok(authService.register(username, password));
    }
}

2、接收复杂嵌套对象

java 复制代码
// 复杂对象定义
public class OrderRequest {
    private Long userId;
    private List<OrderItem> items;
    private Address shippingAddress;
    private PaymentInfo paymentInfo;
    
    // getters and setters
}

public class OrderItem {
    private Long productId;
    private Integer quantity;
    private BigDecimal price;
    
    // getters and setters
}

@RestController
public class OrderController {
    
    // 接收复杂嵌套对象
    // 请求体:{
    //   "userId": 123,
    //   "items": [
    //     {"productId": 1, "quantity": 2, "price": 99.99},
    //     {"productId": 2, "quantity": 1, "price": 149.99}
    //   ],
    //   "shippingAddress": {"street": "123 Main St", "city": "New York"},
    //   "paymentInfo": {"cardNumber": "4111111111111111", "expiry": "12/25"}
    // }
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest orderRequest) {
        return orderService.createOrder(orderRequest);
    }
}

注意,使用 @RequestBody 注解时需要确保请求体数据的格式与方法参数类型匹配。例如,如果控制器方法的参数是 String 类型,那么请求体中的数据应该是纯文本格式的。

总之,@RequestBody 注解是用于获取 POST 请求体中的数据并在控制器方法中使用的重要注解,可以方便地将请求体中的数据转换成 Java 对象并进行相应的处理。

五、@PathVariable、@RequestBody、@RequestParam注解之间的区别


@RequestParam 用于从HTTP请求的查询参数或表单数据中提取值到控制器方法的参数中。
@PathVariable 用于从URL路径模板中提取值到控制器方法的参数中,是RESTful API设计的核心注解。
@RequestBody 用于将 HTTP请求体 中的数据绑定到方法的参数上,主要用于处理JSON、XML等格式的请求数据。

@PathVariable:URL路径中的资源ID(/users/123)

@RequestParam:URL问号后的查询条件(/users?name=john)

@RequestBody:请求体中的复杂数据(JSON对象)

相关推荐
R***62311 天前
Spring Boot 整合 log4j2 日志配置教程
spring boot·单元测试·log4j
百花~6 天前
自动化测试概念篇~
selenium·log4j
k***3888 天前
SpringBoot Test详解
spring boot·后端·log4j
马达加斯加D9 天前
C# --- 如何写UT
前端·c#·log4j
秃了也弱了。9 天前
testng:Java界功能强大的单元测试框架
java·单元测试·log4j
q***420512 天前
Spring Boot 整合 log4j2 日志配置教程
spring boot·单元测试·log4j
m0_5656111317 天前
Java高级特性:单元测试、反射、注解、动态代理
java·单元测试·log4j
LSL666_17 天前
Spring 框架整合 JUnit 单元测试——包含完整执行流程
spring·junit·log4j
郝开17 天前
Spring Boot 2.7.18(最终 2.x 系列版本)8 - 日志:Log4j2 基本概念;Log4j2 多环境日志配置策略
spring boot·单元测试·log4j