中 基础
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解决循环依赖的流程:
创建 A:
1. 实例化 A(new A(),此时 a.b = null)
2. 将 A 的工厂放入三级缓存:singletonFactories.put("A", () -> A实例 or AOP代理)
3. 填充 A 的属性,发现需要 B → 触发 B 的创建
创建 B:
1. 实例化 B(new B(),此时 b.a = null)
2. 将 B 的工厂放入三级缓存
3. 填充 B 的属性,发现需要 A
→ 查一级缓存:没有
→ 查二级缓存:没有
→ 查三级缓存:找到 A 的 ObjectFactory → 调用 getObject() 获取 A 的早期引用
→ 将 A 的早期引用放入二级缓存,删除三级缓存中 A 的工厂
→ b.a = A的早期引用(A虽不完整但已有内存地址)
4. B 初始化完成 → 放入一级缓存
回到 A 的属性填充:
a.b = B(完整的B)
4. A 初始化完成 → 放入一级缓存(同时清理二级缓存中 A 的早期引用)plaintext为什么需要三级缓存而不是两级?
- 二级缓存中存的是早期暴露的 Bean 引用(可能是原始对象,也可能是 AOP 代理)
- 三级缓存中存的是 ObjectFactory(工厂),通过工厂可以延迟创建 AOP 代理
- 如果 Bean A 需要被 AOP 代理,其他 Bean 注入的应该是代理对象,而不是原始对象
- 三级缓存的 ObjectFactory 负责:如果 A 需要 AOP 代理,则返回代理对象;否则返回原始对象
- 这样保证了循环依赖时注入的始终是正确的(代理/非代理)对象
循环依赖无法解决的情况:
- 构造器注入的循环依赖(实例化阶段就需要依赖,三级缓存来不及介入)
- 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 单例