Skip to content

控制器声明 (Declaration)

你可以通过在 Servlet 的 WebApplicationContext 中使用标准的 Spring Bean 定义来定义控制器 Bean。@Controller 刻板注解(Stereotype)允许自动检测,这与 Spring 对类路径中 @Component 类的通用支持是一致的。它同时也作为被注解类的标识,表明其作为 Web 组件的角色。

启用自动检测

要启用此类 @Controller Bean 的自动检测,你可以将组件扫描添加到 Java 配置中:

java
@Configuration
@ComponentScan("org.example.web")
public class WebConfiguration {
	// ...
}
kotlin
@Configuration
@ComponentScan("org.example.web")
class WebConfiguration {
	// ...
}
xml
<beans ...>
	<context:component-scan base-package="org.example.web"/>
</beans>

@RestController 是一个组合注解,它本身被 @Controller@ResponseBody 元注解,表示该控制器的每个方法都会继承类型级别的 @ResponseBody 注解,因此会直接写入响应体,而不是进行视图解析。

AOP 代理

在某些情况下,你可能需要在运行时用 AOP 代理装饰控制器。例如,如果你选择在控制器上直接使用 @Transactional 注解。对于控制器,我们建议使用基于类的代理。当注解直接在控制器上时,这通常是自动发生的。

如果控制器实现了一个接口,并且需要 AOP 代理,你可能需要显式配置基于类的代理。例如:

  • 使用 @EnableTransactionManagement(proxyTargetClass = true)
  • 使用 <tx:annotation-driven proxy-target-class="true"/>

注意

从 6.0 版本开始,在使用接口代理时,Spring MVC 不再仅基于接口上的类型级 @RequestMapping 注解来检测控制器。请启用基于类的代理,或者接口本身也必须具有 @Controller 注解。


补充教学

1. 扫描路径的考量

为什么文档中特意提到 base-package="org.example.web"? 这是为了分层扫描

  • Servlet 上下文(Web):通常只扫描 @ControllerWebMvcConfigurer 等 Web 相关的组件。
  • 根上下文(Root):扫描 @Service@Repository 等业务和持久层组件。 这样做可以避免在 Web 层和业务层之间产生 Bean 的重复加载或循环依赖。

2. 为什么 @RestController 更好写?

在没有 @RestController 时,如果你写一个 REST 接口,你需要在每个方法上都写 @ResponseBody。 组合注解极大地减少了样板代码。它明确了“数据即响应”的职责。

3. AOP 代理的“坑”:proxy-target-class

如果你在 Controller 上使用了 Spring Security 的注解(如 @PreAuthorize)或者 @Transactional,Spring 会创建一个代理对象。

  • JDK 动态代理:要求 Controller 必须实现接口。
  • CGLIB 代理(基于类的代理):不需要接口。 推荐:在现代开发中(尤其是 Spring Boot 中),默认就是使用 CGLIB 代理。这避免了因缺少接口而导致的拦截失效或类型转换错误。

Based on Spring Framework.