Spring MVC核心注解与实践指南
- 发布时间:2025-03-07 15:26:36
- 本文热度:浏览 27 赞 0 评论 0
- 文章标签: Spring MVC Java Web开发 注解详解
- 全文共1字,阅读约需1分钟
@RequestMapping注解详解
@RequestMapping是Spring MVC中最基础且使用频率最高的注解,用于将HTTP请求映射到控制器方法。该注解可作用于类级别和方法级别,支持多种属性配置:
@Controller
@RequestMapping("/products")
public class ProductController {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getProduct(@PathVariable Long id, Model model) {
Product product = productService.findById(id);
model.addAttribute("product", product);
return "productDetail";
}
@RequestMapping(value = "/create",
method = {RequestMethod.GET, RequestMethod.POST},
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
Product savedProduct = productService.save(product);
return new ResponseEntity<>(savedProduct, HttpStatus.CREATED);
}
}
核心属性解析:
value/path
:定义请求路径映射,支持Ant风格路径模式method
:指定支持的HTTP方法类型(GET/POST等)params
:要求请求必须包含特定参数headers
:限制请求头必须满足特定条件consumes
:指定处理请求的媒体类型(如application/json)produces
:指定响应输出的媒体类型
开发技巧:
- 类级别定义基础路径,方法级别定义具体端点
- 使用组合注解(@GetMapping等)简化代码
- 路径参数使用
{}
语法配合@PathVariable - 媒体类型协商优先使用produces/consumes
参数绑定注解簇
@RequestParam参数绑定
处理查询参数和表单数据的标准方式:
@GetMapping("/search")
public String searchProducts(
@RequestParam(name = "keyword", required = false) String searchTerm,
@RequestParam(defaultValue = "1") int page,
@RequestParam(required = false) String sortBy) {
// 业务逻辑
}
参数选项:
required
:是否必须参数(默认true)defaultValue
:参数默认值name/value
:参数别名
@PathVariable路径变量
绑定URI模板变量到方法参数:
@DeleteMapping("/{category}/{id}")
public ResponseEntity deleteItem(
@PathVariable String category,
@PathVariable Long id) {
// 删除操作
}
最佳实践:
- 保持路径变量名称与方法参数名一致
- 复杂类型需要自定义Converter
- 配合正则表达式进行参数验证:
@GetMapping("/{id:\\d+}")
@RequestBody请求体处理
处理非表单类型的请求内容(JSON/XML):
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User savedUser = userService.create(user);
return ResponseEntity.created(URI.create("/users/" + savedUser.getId()))
.body(savedUser);
}
注意事项:
- 需要配置消息转换器(如MappingJackson2HttpMessageConverter)
- 配合@Valid进行参数验证
- 不支持multipart/form-data类型
模型处理注解
@ModelAttribute的三种用法
- 方法参数绑定:
@PostMapping("/order")
public String submitOrder(@ModelAttribute("orderForm") Order order) {
// 处理订单
}
- 方法级别属性添加:
@ModelAttribute("categories")
public List<Category> loadCategories() {
return categoryService.getAll();
}
- 返回值隐式绑定:
@ModelAttribute
public User currentUser() {
return userService.getCurrentUser();
}
@SessionAttributes会话管理
@Controller
@SessionAttributes("shoppingCart")
public class CartController {
@ModelAttribute("shoppingCart")
public ShoppingCart initializeCart() {
return new ShoppingCart();
}
@PostMapping("/cart/add")
public String addItem(@ModelAttribute ShoppingCart cart,
@RequestParam Item item) {
cart.addItem(item);
return "redirect:/cart";
}
}
注意点:
- 需配合@SessionAttribute注解清除属性
- 会话超时处理策略
- 分布式会话存储问题
响应处理注解
@ResponseBody与HttpMessageConverter
@GetMapping(value = "/report", produces = MediaType.APPLICATION_PDF_VALUE)
@ResponseBody
public byte[] generateReport() {
return pdfService.generateReport();
}
转换器配置示例:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
@RestController复合注解
等效于@Controller + @ResponseBody:
@RestController
@RequestMapping("/api/users")
public class UserApiController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
验证注解与错误处理
参数验证流程
@PostMapping("/register")
public String registerUser(@Valid @ModelAttribute UserForm form,
BindingResult result) {
if (result.hasErrors()) {
return "registrationForm";
}
userService.register(form);
return "redirect:/welcome";
}
常用验证注解:
- @NotNull/@NotEmpty
- @Size(min=2, max=30)
- @Pattern(regexp="正则表达式")
- @Future/@Past
自定义验证器示例
public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {
private Pattern pattern = Pattern.compile("^1[3-9]\\d{9}$");
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && pattern.matcher(value).matches();
}
}
异常处理机制
@ExceptionHandler控制器级异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((FieldError) error).getField();
String message = error.getDefaultMessage();
errors.put(fieldName, message);
});
return ResponseEntity.badRequest().body(errors);
}
}
RESTful支持注解
@ResponseStatus状态码控制
@ResponseStatus(code = HttpStatus.CREATED, reason = "Resource created")
public class CreatedException extends RuntimeException {}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteResource(@PathVariable Long id) {
resourceService.delete(id);
}
HATEOAS支持示例
@GetMapping("/{id}")
public EntityModel<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return EntityModel.of(user,
linkTo(methodOn(UserController.class).getUser(id)).withSelfRel(),
linkTo(methodOn(UserController.class).getAllUsers()).withRel("users"));
}
跨域处理注解
@CrossOrigin配置示例
@RestController
@CrossOrigin(origins = "https://trusted-domain.com",
maxAge = 3600,
allowedHeaders = {"x-requested-with", "content-type"},
methods = {RequestMethod.GET, RequestMethod.POST})
@RequestMapping("/api")
public class ApiController {
// 控制器方法
}
测试验证代码示例
MockMVC测试用例
@SpringBootTest
@AutoConfigureMockMvc
class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetProduct() throws Exception {
mockMvc.perform(get("/products/123")
.header("Accept", "application/json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Test Product"));
}
@Test
void testCreateProduct() throws Exception {
String jsonBody = "{ \"name\":\"New Product\", \"price\":99.99 }";
mockMvc.perform(post("/products")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonBody))
.andExpect(status().isCreated())
.andExpect(header().exists("Location"));
}
}
性能优化建议
- 合理使用@RequestMapping的窄化配置
- 优先使用组合注解提升代码可读性
- 批量参数绑定使用@ModelAttribute
- 异步处理使用@Async和Callable
- 缓存静态资源映射配置
- 合理设置消息转换器顺序
正文到此结束
相关文章
热门推荐
评论插件初始化中...