深度解析 Spring MVC @Controller
注解
@Controller
是 Spring MVC 框架中最核心的注解之一,用于标记一个类作为 Web 请求处理器。本文将深入剖析其工作原理、源码实现、使用场景及最佳实践。
一、注解定义与核心作用
1. 源码定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
2. 核心特性
- 继承自
@Component
:被@Controller
标记的类会被 Spring 容器扫描并注册为 Bean - MVC 专用组件:标识该类作为 Web 请求处理器
- 名称指定:通过
value
属性指定 Bean 名称 - 请求映射:需配合
@RequestMapping
等注解定义具体请求处理方法
二、工作原理与请求处理流程
1. Spring MVC 请求处理全流程
2. @Controller
的核心作用阶段
- 组件扫描:Spring 容器扫描并注册
@Controller
类为 Bean - 请求映射:
HandlerMapping
建立 URL 到控制器方法的映射 - 请求处理:
HandlerAdapter
调用控制器方法处理请求 - 结果处理:处理返回值并渲染视图
三、控制器方法详解
1. 方法签名组成要素
@Controller
@RequestMapping("/products")
public class ProductController {
@GetMapping("/{id}")
public String getProduct(
@PathVariable Long id, // URL路径变量
@RequestParam(name = "details", defaultValue = "false") boolean showDetails, // 请求参数
@RequestHeader("User-Agent") String userAgent, // 请求头
@RequestBody ProductDTO productDTO, // 请求体
Model model // 模型数据
) {
// 业务逻辑
model.addAttribute("product", productService.findById(id));
return "product/detail"; // 视图名称
}
}
2. 支持的参数类型
参数类型 | 说明 | 示例 |
---|---|---|
@PathVariable | 获取URL路径变量 | @PathVariable Long id |
@RequestParam | 获取请求参数 | @RequestParam String name |
@RequestHeader | 获取请求头 | @RequestHeader("Accept") String accept |
@RequestBody | 获取请求体 | @RequestBody User user |
@ModelAttribute | 获取模型属性 | @ModelAttribute("user") User user |
HttpServletRequest/Response | 原生请求/响应对象 | HttpServletRequest request |
Model/ModelMap | 模型数据容器 | Model model |
SessionStatus | 会话状态 | SessionStatus status |
3. 支持的返回值类型
返回值类型 | 说明 | 示例 |
---|---|---|
String | 视图名称 | return "product/list" |
ModelAndView | 包含模型和视图的对象 | return new ModelAndView("home", model) |
View | 视图对象 | return new MappingJackson2JsonView() |
void | 直接写响应 | response.getWriter().write("OK") |
ResponseEntity | 完整响应实体 | return ResponseEntity.ok(body) |
@ResponseBody | 直接返回数据 | @ResponseBody Product get() |
四、源码深度解析
1. 控制器扫描与注册
ClassPathBeanDefinitionScanner
负责扫描 @Controller
注解:
public class ClassPathBeanDefinitionScanner {
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// 检查是否带有@Component注解(包括元注解)
AnnotationMetadata metadata = beanDefinition.getMetadata();
return metadata.isAnnotated(Component.class.getName());
}
}
2. 请求映射处理
RequestMappingHandlerMapping
处理控制器方法的映射:
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, RequestCondition<?> customCondition) {
// 构建RequestMappingInfo对象
return RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(customCondition)
.build();
}
}
3. 控制器方法调用
RequestMappingHandlerAdapter
负责调用控制器方法:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter {
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 创建可调用的处理器方法
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
// 设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
// 执行方法调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
}
五、@Controller
vs @RestController
1. 核心区别
特性 | @Controller | @RestController |
---|---|---|
继承关系 | @Component | @Controller + @ResponseBody |
主要用途 | 传统MVC视图渲染 | RESTful API |
返回值处理 | 返回视图名称 | 直接返回数据 |
内容协商 | 支持多种视图解析 | 基于HTTP Accept头 |
典型场景 | 服务端渲染页面 | 前后端分离API |
2. @RestController
源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(annotation = Controller.class)
String value() default "";
}
六、高级特性与最佳实践
1. 控制器增强(@ControllerAdvice
)
全局异常处理:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<String> handleProductNotFound(ProductNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralException(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Server error");
}
}
2. 方法参数解析器扩展
自定义参数解析器:
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(User.class);
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
return (User) request.getSession().getAttribute("currentUser");
}
}
// 注册解析器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new UserArgumentResolver());
}
}
3. RESTful 控制器设计
@RestController
@RequestMapping("/api/products")
public class ProductApiController {
@Autowired
private ProductService productService;
@GetMapping
public ResponseEntity<List<Product>> getAllProducts() {
return ResponseEntity.ok(productService.findAll());
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
return productService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
Product saved = productService.save(product);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(saved.getId())
.toUri();
return ResponseEntity.created(location).body(saved);
}
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(
@PathVariable Long id, @RequestBody Product product) {
return ResponseEntity.ok(productService.update(id, product));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
productService.delete(id);
return ResponseEntity.noContent().build();
}
}
七、性能优化策略
1. 控制器设计原则
- 单一职责:每个控制器只负责一个业务领域
- 精简方法:控制器方法应保持简洁,委托给Service层
- 避免阻塞:长时间操作使用异步处理
- 合理缓存:对静态数据使用缓存
2. 异步控制器
@Controller
public class AsyncController {
@GetMapping("/async")
@ResponseBody
public Callable<String> asyncProcessing() {
return () -> {
// 长时间操作
Thread.sleep(3000);
return "Async result";
};
}
@GetMapping("/deferred")
@ResponseBody
public DeferredResult<String> deferredResult() {
DeferredResult<String> result = new DeferredResult<>();
CompletableFuture.runAsync(() -> {
// 异步处理
try {
Thread.sleep(2000);
result.setResult("Deferred result");
} catch (InterruptedException e) {
result.setErrorResult(e);
}
});
return result;
}
}
3. 响应压缩配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.favorPathExtension(true)
.ignoreAcceptHeader(false)
.defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
@Bean
public FilterRegistrationBean<GzipFilter> gzipFilter() {
FilterRegistrationBean<GzipFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new GzipFilter());
registration.addUrlPatterns("/*");
return registration;
}
}
八、常见问题解决方案
1. 404 控制器未找到
解决方案:
// 确保组件扫描包含控制器包
@SpringBootApplication(scanBasePackages = "com.example.controllers")
public class Application { ... }
// 检查请求路径匹配
@Controller
@RequestMapping("/products") // 确保路径正确
public class ProductController { ... }
2. 参数绑定失败
解决方案:
@PostMapping
public String createProduct(@Valid ProductForm form, BindingResult result) {
if (result.hasErrors()) {
// 处理验证错误
return "products/new";
}
// 保存逻辑
return "redirect:/products";
}
3. 跨域问题
解决方案:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
九、最佳实践总结
1. 控制器设计规范
- 命名规范:
XxxController
形式命名 - URL设计:RESTful 风格资源路径
- 方法精简:控制器方法不超过50行代码
- 异常处理:使用
@ControllerAdvice
统一处理异常 - 安全控制:结合
@Secured
或@PreAuthorize
进行权限控制
2. 现代架构演进
传统MVC架构:
前后端分离架构:
3. 未来发展方向
- 响应式编程:Spring WebFlux 的
@Controller
变体 - 函数式端点:RouterFunction 替代注解式控制器
- GraphQL集成:更灵活的数据查询
- Serverless支持:轻量级控制器函数
十、总结
@Controller
是 Spring MVC 的核心组件,其关键点包括:
- 组件标识:通过
@Component
元注解实现自动扫描 - 请求处理:配合
@RequestMapping
定义请求处理方法 - 灵活参数:支持多种参数绑定方式
- 多视图支持:可返回多种视图类型
- 扩展性强:可通过多种方式扩展功能
在现代应用开发中:
- 传统 Web 应用使用
@Controller
+ 视图技术 - RESTful API 使用
@RestController
- 响应式应用使用 WebFlux 的
@Controller
掌握 @Controller
的工作原理和最佳实践,能够帮助开发者构建出结构清晰、维护性强的 Web 应用。随着 Spring 框架的发展,控制器编程模型也在不断演进,但 @Controller
作为基础组件的地位仍不可替代。