跳到主要内容

Spring 常用注解及其底层原理解析

在现代 Spring/Spring Boot 开发中,基于注解的声明式配置已成为绝对的主流。理解这些常用注解的底层工作机制,能够帮助我们更好地排查依赖注入、单例失效等日常开发中的棘手问题。


一、 核心装配注解:@Autowired 与 @Resource

这两个注解都用于依赖注入,但它们的设计背景、注入规则及底层实现机制大相径庭。

1. 核心差异对比

特征@Autowired@Resource
来源规范Spring 框架原生提供JSR-250 规范(Jakarta EE),JDK 内置(JDK 9+ 被移出,需引入依赖)
默认装配规则byType(按类型)。若有多个同类型 Bean,再按属性名作为名称装配byName(按名称)。若找不到名称对应的 Bean,才退化为按类型装配
配置属性required (默认为 true),@Qualifier (指定名称),@Primary (指定优先级)name (显式指定名称),type (显式指定类型)
底层实现类AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor

2. @Autowired 注入原理

AutowiredAnnotationBeanPostProcessor 是一个 InstantiationAwareBeanPostProcessor

在 Bean 的生命周期中,在 populateBean() 阶段,Spring 会调用此后置处理器的 postProcessProperties() 方法:

  1. 寻找注入点:扫描当前类中的所有字段和方法,寻找标注了 @Autowired@Value 的成员,封装为 AutowiredFieldElementAutowiredMethodElement 并缓存。
  2. 解析依赖:调用 BeanFactory.resolveDependency(),根据字段类型在容器中查找对应的目标 Bean。
  3. 反射注入:利用 Java 反射(设置 Field.setAccessible(true)),将解析出来的 Bean 强行赋值给字段或通过方法反射注入。

3. @Resource 注入原理

CommonAnnotationBeanPostProcessor 继承自 InitDestroyAnnotationBeanPostProcessor

postProcessProperties() 阶段,它的处理流程如下:

  1. 按名称查找:若显式配置了 @Resource(name="..."),或者默认以属性名作为 Bean 名称,到容器中调用 beanFactory.getBean(beanName)。如果找到,则直接返回。
  2. 按类型 fallback:如果在容器中找不到名称匹配的 Bean,且未显式指定 name 属性,则回退为按类型去容器中解析,逻辑等同于 @AutowiredresolveDependency()

二、 声明 Bean 的注解:@Component 系列与 @Bean

1. @Component、@Repository、@Service、@Controller

这四个注解在功能上基本等价,均用于将类标识为 Spring 管理的组件(即注册为 BeanDefinition),但它们的语义层次和部分底层逻辑有所区别:

  • @Component:通用组件注解,可用于任何层级的类。
  • @Service:标注在业务逻辑层,当前无额外特殊逻辑。
  • @Repository:标注在数据访问层(DAO)。Spring 会为其自动织入 PersistenceExceptionTranslationPostProcessor,将底层数据库抛出的特定异常(如 SQLException)统一转化为 Spring 的 DataAccessException 体系。
  • @Controller:标注在控制层。Spring MVC 在启动扫描时,RequestMappingHandlerMapping 会通过判断类上是否含有 @Controller@RequestMapping,来决定是否将其中的方法解析为处理器映射接口(参见 RequestMappingHandlerMapping 映射原理)。

2. @Component 与 @Bean 的区别

  • 作用对象@Component 作用于@Bean 作用于配置类中的方法
  • 掌控力度@Component 适合用于我们自己编写的类,Spring 会通过类路径扫描(@ComponentScan)自动实例化它们;@Bean 用于我们要引入第三方库的类(我们无法在别人的类上加 @Component 注解),或者需要以极其复杂的自定义逻辑来构造对象时。

三、 配置类注解:@Configuration 深度工作机制

1. proxyBeanMethods:Full 模式与 Lite 模式

@Configuration 注解中,有一个非常关键的属性:

public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";

boolean proxyBeanMethods() default true; // 默认为 true
}

Full 模式 (proxyBeanMethods = true)

  • 底层动作:Spring 会使用 CGLIB 为该配置类生成一个动态代理类。
  • 作用:保证配置类中 @Bean 方法之间相互调用时的单例特性
  • 原理解析: 在配置类中,方法 A 和方法 B 都标注了 @Bean。如果方法 A 内部直接调用了 B(),由于配置类被 CGLIB 代理了,调用 A() 时触发的 B() 不会执行 B() 方法的方法体本身,而是会被代理类拦截,转而执行 beanFactory.getBean("B")。因此,即使多次调用 B(),拿到的也是同一个 Bean 实例。

Lite 模式 (proxyBeanMethods = false)

  • 底层动作:Spring 不会为配置类生成 CGLIB 代理,将其作为普通的 @Component 类看待。
  • 作用:避免了 CGLIB 动态生成字节码的开销,提升了 Spring 启动性能,降低了内存占用。
  • 代价:方法 A 内部调用 B() 时,就仅仅是普通的 Java 方法调用,会直接在堆上 new 出一个新的普通对象,脱离了 Spring IoC 容器的管理,导致单例失效。
  • 最佳实践:当配置类中的各个 @Bean 方法之间没有相互调用依赖时,强烈建议设置为 proxyBeanMethods = false

2. @Import 自动装配的核心支撑

@Import 是 Spring 动态注册 Bean 的利器,也是 Spring Boot 自动装配的底层支柱。它支持导入以下三类对象:

  1. 普通类:直接作为组件注册进 Spring 容器。
  2. ImportSelector 接口实现类
    • 核心方法:selectImports(AnnotationMetadata importingClassMetadata)
    • 工作机制:根据配置逻辑动态返回一个需要导入的类名数组,Spring 会自动将这些类注册为 BeanDefinition。
  3. ImportBeanDefinitionRegistrar 接口实现类
    • 核心方法:registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
    • 工作机制:直接暴露了 BeanDefinitionRegistry 注册器,开发者可以通过 BeanDefinitionBuilder 手动组装并注册任何复杂的 Bean 定义。

关于这部分的详细流程和生命周期触发时机,可以参考 BeanDefinition 加载原理


四、 生命周期回调注解:@PostConstruct 与 @PreDestroy

1. 底层实现原理

这两个注解属于 JSR-250 规范,Spring 是通过 CommonAnnotationBeanPostProcessor(及其父类 InitDestroyAnnotationBeanPostProcessor)来支持它们的。

  • @PostConstruct:在 Bean 实例化和属性赋值(Populate)完毕后,在初始化(Initialization)阶段执行。
  • @PreDestroy:在 Spring 容器关闭、销毁 Bean 实例时触发。

2. 在整个生命周期中的执行顺序

结合 ioc-aop.md 中的 Bean 生命周期,当一个 Bean 初始化时,各方法的执行顺序如下:

  1. 构造器实例化(Constructor)
  2. 属性赋值@Autowired 依赖注入)
  3. Aware 接口回调(如 BeanNameAware, BeanFactoryAware
  4. BeanPostProcessor 前置处理
    • 在此阶段,InitDestroyAnnotationBeanPostProcessor 拦截并执行标记了 @PostConstruct 的方法。
  5. InitializingBean 回调
    • 执行 afterPropertiesSet() 方法。
  6. 自定义 init-method
    • 执行配置的 XML/Bean 注解中的 initMethod
  7. BeanPostProcessor 后置处理(AOP 代理在此阶段生成)

当容器销毁时,顺序如下:

  1. DestructionAwareBeanPostProcessor 拦截执行标记了 @PreDestroy 的方法。
  2. DisposableBean 回调:执行 destroy()
  3. 自定义 destroy-method

总结

Spring 的注解机制本质上是一套基于 BeanPostProcessorBeanFactoryPostProcessor 的元数据解析与拦截体系。

  • 通过 AutowiredAnnotationBeanPostProcessor 实现了优雅的依赖注入;
  • 通过 ConfigurationClassPostProcessor(CGLIB 代理)保证了配置类方法调用的单例语义;
  • 通过 InitDestroyAnnotationBeanPostProcessor 串联起了 JSR-250 规范的生命周期回调。

深入理解这些注解的运作规律,是编写高质量、无 Bug 的 Spring 代码的基础。