面试知识库
基础

Spring常用注解总结#

一句话答案#

核心注解:@Component/@Service/@Repository(Bean 注册)、@Autowired(注入)、@Transactional(事务)、@Configuration+@Bean(配置)。

核心要点

循环依赖场景:

@Component
class A {
    @Autowired B b;  // A 依赖 B
}

@Component
class B {
    @Autowired A a;  // B 依赖 A → 循环!
}
java

三级缓存的数据结构:

// 一级缓存:存放完全初始化好的单例 Bean
Map<String, Object> singletonObjects;

// 二级缓存:存放早期暴露的 Bean(已实例化但未完成属性填充)
Map<String, Object> earlySingletonObjects;

// 三级缓存:存放 Bean 的工厂对象(ObjectFactory,用于创建早期 Bean 引用)
Map<String, ObjectFactory<?>> singletonFactories;
java

解决循环依赖的流程:

为什么需要三级缓存而不是两级?

  • 二级缓存中存的是早期暴露的 Bean 引用(可能是原始对象,也可能是 AOP 代理)
  • 三级缓存中存的是 ObjectFactory(工厂),通过工厂可以延迟创建 AOP 代理
  • 如果 Bean A 需要被 AOP 代理,其他 Bean 注入的应该是代理对象,而不是原始对象
  • 三级缓存的 ObjectFactory 负责:如果 A 需要 AOP 代理,则返回代理对象;否则返回原始对象
  • 这样保证了循环依赖时注入的始终是正确的(代理/非代理)对象

循环依赖无法解决的情况:

  1. 构造器注入的循环依赖(实例化阶段就需要依赖,三级缓存来不及介入)
  2. prototype(原型)Bean 的循环依赖(不缓存,每次创建新实例)
面试回答(2分钟版)

Spring常用注解可以分三组来记。第一组是Bean注册:@Component是通用注解,@Service、@Repository、@Controller是它的语义化派生,功能相同但表达不同的架构层次,@Repository额外提供数据访问异常转换。第二组是依赖注入:@Autowired按类型注入,存在多个同类型Bean时配合@Qualifier按名称指定;@Resource是JSR-250标准注解,默认按名称注入。第三组是配置类:@Configuration标注配置类,内部@Bean方法手动注册第三方库的类,@Configuration保证@Bean方法之间互相调用返回的是同一个单例,不加只是lite模式不保证单例。另外循环依赖是高频追问:Spring通过三级缓存解决Singleton Bean的字段注入循环依赖,一级缓存存完整Bean,二级存早期引用,三级存ObjectFactory延迟创建AOP代理。但构造器注入的循环依赖和Prototype Bean的循环依赖无法解决。

追问与易错

追问方向:

  • “@Component 和 @Bean 区别?”→ @Component 标注在类上由组件扫描自动注册,适合自己写的类;@Bean 标注在方法上手动创建实例注册,适合第三方库的类(无法加注解)
  • “@Autowired 和 @Resource 区别?”→ @Autowired 是 Spring 注解默认按类型(byType)注入,类型冲突配合 @Qualifier;@Resource 是 JSR-250 标准注解默认按名称(byName)注入,更具可移植性
  • “@Configuration 和 @Component 区别?”→ @Configuration 类中 @Bean 方法互相调用时返回同一个单例实例(Full 模式,通过 CGLIB 代理拦截);@Component 类中 @Bean 方法互相调用会创建新实例(Lite 模式,不保证单例)

易错点:

  • ❌ @Component/@Service/@Repository 完全一样——语义不同
  • ❌ @Configuration 不加也行——lite mode 不保证 @Bean 单例