SpringMVC核心注解与最佳实践指南

一、核心请求处理注解体系

1.1 @Controller与@RestController

在SpringMVC框架中,控制器是处理HTTP请求的核心组件。@Controller注解用于标记类为控制器:

@Controller
public class UserController {
    @RequestMapping("/users")
    public String listUsers(Model model) {
        model.addAttribute("userList", userService.getAllUsers());
        return "user/list";
    }
}

@RestController是Spring 4.0引入的组合注解,等价于@Controller + @ResponseBody:

@RestController
@RequestMapping("/api/users")
public class UserApiController {
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
}

开发建议:

  • Web页面交互优先使用@Controller
  • RESTful API优先使用@RestController
  • 注意模板引擎与JSON序列化的区别

1.2 请求映射注解家族

Spring 4.3引入了更语义化的请求方法注解:

注解 等价形式 HTTP方法
@GetMapping @RequestMapping(method=GET) GET
@PostMapping @RequestMapping(method=POST) POST
@PutMapping @RequestMapping(method=PUT) PUT
@DeleteMapping @RequestMapping(method=DELETE) DELETE

使用示例:

@RestController
@RequestMapping("/products")
public class ProductController {
    
    @GetMapping("/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productService.findById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Product createProduct(@RequestBody Product product) {
        return productService.save(product);
    }
}

1.3 复杂路径匹配

路径匹配支持Ant风格和正则表达式:

@GetMapping("/files/{fileId:[a-f0-9]{32}}")
public ResponseEntity<byte[]> getFile(@PathVariable String fileId) {
    // 处理32位哈希值的文件请求
}

@GetMapping("/images/**")
public String handleImages() {
    // 匹配/images/开头的所有路径
}

二、参数绑定深度解析

2.1 @RequestParam高级用法

@GetMapping("/search")
public Page<User> searchUsers(
    @RequestParam(defaultValue = "") String keyword,
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "20") int size) {
    // 分页查询逻辑
}

参数处理技巧:

  • 设置默认值避免NullPointerException
  • 使用Optional包装可能缺失的参数
  • 通过required属性控制参数必填性

2.2 @PathVariable类型转换

自定义类型转换示例:

@GetMapping("/orders/{orderNo}")
public Order getOrder(@PathVariable("orderNo") Order order) {
    return order;
}

// 自定义转换器
@Component
public class OrderConverter implements Converter<String, Order> {
    @Override
    public Order convert(String orderNo) {
        return orderService.findByNo(orderNo);
    }
}

2.3 @RequestBody处理策略

复杂JSON绑定示例:

@PostMapping("/complex")
public ResponseData handleComplex(@Valid @RequestBody ComplexDTO dto) {
    // 处理多层嵌套对象
}

// DTO定义
public class ComplexDTO {
    @NotNull
    private UserInfo user;
    
    @NotEmpty
    private List<OrderItem> items;
    
    // 嵌套对象验证
    public static class UserInfo {
        @NotBlank
        private String name;
        @Email
        private String email;
    }
}

三、响应处理机制

3.1 视图解析技术

传统MVC视图解析流程:

@Controller
public class ReportController {
    @GetMapping("/monthly-report")
    public ModelAndView generateReport() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("reportData", reportService.generate());
        mav.setViewName("reports/monthly");
        return mav;
    }
}

视图解析器配置示例:

# Thymeleaf配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false

3.2 ResponseEntity精细控制

完全控制HTTP响应:

@GetMapping("/download")
public ResponseEntity<Resource> downloadFile() throws IOException {
    Path filePath = Paths.get("/data/files/report.pdf");
    Resource resource = new InputStreamResource(Files.newInputStream(filePath));

    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"report.pdf\"")
        .contentType(MediaType.APPLICATION_PDF)
        .contentLength(Files.size(filePath))
        .body(resource);
}

3.3 全局响应包装

统一响应格式处理:

@RestControllerAdvice
public class ResponseWrapper implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, 
                          Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                MediaType selectedContentType,
                                Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ApiResponse) {
            return body;
        }
        return new ApiResponse<>(200, "success", body);
    }
}

四、模型处理机制

4.1 @ModelAttribute进阶

方法级@ModelAttribute的预加载:

@ModelAttribute("categories")
public List<Category> loadCategories() {
    return categoryService.getAllActiveCategories();
}

@ModelAttribute
public void preloadModel(Model model) {
    model.addAttribute("currentUser", userService.getCurrentUser());
}

表单绑定示例:

<!-- Thymeleaf模板 -->
<form th:action="@{/products}" method="post" th:object="${product}">
    <input type="text" th:field="*{name}">
    <input type="number" th:field="*{price}">
</form>

4.2 会话管理策略

@SessionAttributes使用模式:

@Controller
@SessionAttributes("checkoutInfo")
public class CheckoutController {
    
    @GetMapping("/checkout/step1")
    public String step1(Model model) {
        model.addAttribute("checkoutInfo", new CheckoutInfo());
        return "checkout/step1";
    }

    @PostMapping("/checkout/step2")
    public String step2(@ModelAttribute CheckoutInfo checkoutInfo) {
        return "checkout/step2";
    }
}

会话超时配置:

server.servlet.session.timeout=1800  # 30分钟

五、拦截与切面处理

5.1 拦截器深度集成

自定义拦截器示例:

public class AuditInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) {
        log.info("Request [{}] from {}", 
                request.getRequestURI(), 
                request.getRemoteAddr());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
                         HttpServletResponse response,
                         Object handler, 
                         ModelAndView modelAndView) {
        // 可修改ModelAndView
    }
}

// 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuditInterceptor())
                .addPathPatterns("/api/**");
    }
}

5.2 异常处理体系

全局异常处理示例:

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
        return new ErrorResponse("NOT_FOUND", ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleValidationErrors(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList());
        return new ErrorResponse("VALIDATION_FAILED", errors);
    }
}

六、RESTful API最佳实践

6.1 HATEOAS实现

使用Spring HATEOAS构建超媒体API:

@RestController
@RequestMapping("/api/books")
public class BookController {

    @GetMapping("/{id}")
    public EntityModel<Book> getBook(@PathVariable Long id) {
        Book book = bookService.findById(id);
        return EntityModel.of(book,
            linkTo(methodOn(BookController.class).getBook(id)).withSelfRel(),
            linkTo(methodOn(BookController.class).getAllBooks()).withRel("books"));
    }
}

6.2 版本控制策略

三种常用版本控制方式:

  1. URI路径版本控制:
@RestController
@RequestMapping("/api/v1/products")
public class ProductV1Controller { ... }

@RestController
@RequestMapping("/api/v2/products")
public class ProductV2Controller { ... }
  1. 请求头版本控制:
@GetMapping(value = "/products", headers = "X-API-Version=2")
public ProductV2 getProductV2() { ... }
  1. 内容协商版本控制:
@GetMapping(value = "/products", produces = "application/vnd.company.v1+json")
public ProductV1 getProductV1() { ... }

@GetMapping(value = "/products", produces = "application/vnd.company.v2+json")
public ProductV2 getProductV2() { ... }

七、性能优化技巧

7.1 异步处理

DeferredResult使用示例:

@GetMapping("/async")
public DeferredResult<String> asyncRequest() {
    DeferredResult<String> result = new DeferredResult<>();
    
    CompletableFuture.runAsync(() -> {
        try {
            Thread.sleep(3000);
            result.setResult("Async result");
        } catch (InterruptedException e) {
            result.setErrorResult(e.getMessage());
        }
    });
    
    return result;
}

7.2 缓存策略

方法级缓存示例:

@GetMapping("/products/{id}")
@Cacheable(value = "products", key = "#id")
public Product getProduct(@PathVariable Long id) {
    return productService.findById(id);
}

@CacheEvict(value = "products", key = "#product.id")
@PutMapping("/products")
public Product updateProduct(@RequestBody Product product) {
    return productService.update(product);
}

八、安全防护措施

8.1 CSRF防护

Thymeleaf模板自动处理CSRF:

<form method="post">
    <input type="hidden" 
           th:name="${_csrf.parameterName}"
           th:value="${_csrf.token}" />
    <!-- 其他表单字段 -->
</form>

8.2 XSS防护

使用HtmlUtils进行内容转义:

@PostMapping("/comments")
public String postComment(@RequestParam String content) {
    String safeContent = HtmlUtils.htmlEscape(content);
    commentService.save(safeContent);
    return "redirect:/comments";
}

九、测试验证方案

9.1 控制器单元测试

MockMvc测试示例:

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void getUserById() throws Exception {
        given(userService.findById(1L))
            .willReturn(new User(1L, "testuser"));

        mockMvc.perform(get("/users/1"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.username").value("testuser"));
    }
}

9.2 集成测试

SpringBootTest完整测试:

@SpringBootTest
@AutoConfigureMockMvc
class ApplicationIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void contextLoads() throws Exception {
        mockMvc.perform(get("/"))
               .andExpect(status().isOk());
    }
}
正文到此结束
评论插件初始化中...
Loading...