基于注解的容器配置 (Annotation-based Container Configuration)
Spring 对基于注解的配置提供了全面的支持,通过在相关的类、方法或字段声明上使用注解,在组件类本身的元数据上进行操作。正如在示例:AutowiredAnnotationBeanPostProcessor 中提到的,Spring 将 BeanPostProcessors 与注解结合使用,使核心 IoC 容器能够感知特定的注解。
例如,@Autowired 注解提供了与自动装配协作对象中描述的相同功能,但具有更细粒度的控制和更广泛的适用性。此外,Spring 还支持 JSR-250 注解(如 @PostConstruct 和 @PreDestroy),以及 jakarta.inject 包中包含的 JSR-330(Java 依赖注入)注解(如 @Inject 和 @Named)。有关这些注解的详细信息可以在相关章节中找到。
注意
注解注入在外部属性注入之前执行。因此,当通过混合方式进行装配时,外部配置(例如 XML 指定的 Bean 属性)实际上会覆盖针对属性填写的注解。
从技术上讲,你可以将这些后置处理器注册为单独的 Bean 定义,但它们已经隐式注册在 AnnotationConfigApplicationContext 中了。
在基于 XML 的 Spring 设置中,你可以包含以下配置标签,以便与基于注解的配置混合搭配使用:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans><context:annotation-config/> 元素会隐式注册以下后置处理器:
ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessorEventListenerMethodProcessor
注意
<context:annotation-config/> 仅在其定义的同一个应用上下文中查找 Bean 上的注解。这意味着,如果你将 <context:annotation-config/> 放在 DispatcherServlet 的 WebApplicationContext 中,它只检查 Controllers 中的 @Autowired Bean,而不检查 Services。更多信息请参阅 DispatcherServlet。
补充教学 —— 开启“注解时代”的钥匙
1. 为什么会有基于注解的配置? 在 Spring 早期(1.x/2.0),所有的配置都写在厚厚的 XML 里。这导致两个问题:
- 开发效率低:每加一个类都要去改一遍 XML,很繁琐。
- 配置分散:类和它的配置分家了,阅读代码时很难一眼看出这个类需要哪些依赖。 注解的出现让配置“回归”到类本身,代码即配置,更符合开发者的直觉。
2. <context:annotation-config/> 做了什么? 它就像一键开启了 Spring 对注解的“感知能力”。 如果没有它(或者没有对应的 BeanPostProcessor),你在字段上写的 @Autowired 就仅仅是个普通的 Java 注解,Spring 容器会完全无视它。 现在的 Spring Boot 项目中,你不需要写这个标签,因为 AnnotationConfigApplicationContext 已经默认带上了这些能力。
3. BeanPostProcessor 又立功了 正如文档所言,注解不是凭空生效的,它们背后的推手就是 BeanPostProcessor。
@Autowired由AutowiredAnnotationBeanPostProcessor处理。@Resource,@PostConstruct由CommonAnnotationBeanPostProcessor处理。 当这些 BPP 看到一个 Bean 被创建时,它们会扫描类上的注解并执行相应的逻辑(比如把需要的对象塞进字段里)。
4. 覆盖机制:XML 依然是“大哥” 由于注解注入发生在 XML 属性注入之前,这意味着如果你在 XML 里显式配置了一个属性的值,它会把注解自动注入的值 覆盖 掉。 这其实是一个非常有用的特性:你可以在代码里用注解做默认配置,在特定环境下通过 XML 或外部属性做精细化、差异化的“微调”。
5. 容器边界问题 特别注意 WebApplicationContext 的层级。 Spring 应用通常有两个上下文:根上下文 (Root Context) 放 Service 和 Repository,Web 上下文 (Servlet Context) 放 Controller。 如果你在 Web 上下文里开启注解配置,它是看不到根上下文里的 Bean 的注解的(虽然可以使用根上下文里的 Bean 实例,但无法“扫描”到它们的元数据)。这是初学者经常遇到“为什么我的 Controller 没注进去依赖”的原因之一。