Spring 依赖注入原理与循环依赖解决方案
依赖注入是 IoC 的核心实现,循环依赖是 Spring 设计中的重要挑战。
依赖注入方式
三种注入方式对比
| 方式 | 注解 | 特点 | 推荐场景 |
|---|---|---|---|
| 构造器注入 | 无/@Autowired(required=false) | 强依赖、不可变 | 必须依赖 |
| Setter注入 | @Autowired | 可选依赖 | 可选依赖 |
| 字段注入 | @Autowired | 简洁但不推荐 | 不推荐 |
构造器注入(推荐)
Java
@Service
public class UserService {
private final UserRepository userRepository;
private final OrderService orderService;
// 单构造器无需 @Autowired
public UserService(UserRepository userRepository, OrderService orderService) {
this.userRepository = userRepository;
this.orderService = orderService;
}
}
Setter 注入
Java
@Service
public class UserService {
private NotificationService notificationService;
@Autowired(required = false) // 可选依赖
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
}
依赖注入源码解析
AutowiredAnnotationBeanPostProcessor
Java
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
// 处理 @Autowired 和 @Value
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
}
return pvs;
}
}
注入元数据收集
Java
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// 1. 缓存查找
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (metadata == null) {
// 2. 解析类上的 @Autowired 注解
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
return metadata;
}
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
// 扫描字段
ReflectionUtils.doWithLocalFields(clazz, field -> {
if (field.isAnnotationPresent(Autowired.class)) {
elements.add(new AutowiredFieldElement(field, true));
}
});
// 扫描方法
ReflectionUtils.doWithLocalMethods(clazz, method -> {
if (method.isAnnotationPresent(Autowired.class)) {
elements.add(new AutowiredMethodElement(method, true, pvs));
}
});
return new InjectionMetadata(clazz, elements);
}
字段注入实现
Java
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
private final Field field;
private final boolean required;
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) {
// 1. 解析依赖
Object value = resolveDependency(field.getType(), field.getName(), beanName);
// 2. 反射设置值
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
} else if (required) {
throw new BeanCreationException("Required field not found");
}
}
}
循环依赖问题
循环依赖类型
| 类型 | 是否可解决 | 说明 |
|---|---|---|
| 单例 Setter/字段注入 | ✅ 可解决 | 三级缓存 |
| 单例 构造器注入 | ❌ 不可解决 | Lazy 可绕过 |
| Prototype 依赖 | ❌ 不可解决 | 不缓存 |
循环依赖示例
Java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
三级缓存机制
缓存层级
Java
// DefaultSingletonBeanRegistry 核心缓存
// 一级缓存:完整 Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 二级缓存:早期暴露 Bean(已实例化,未初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
// 三级缓存:ObjectFactory(用于生成早期 Bean)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
三级缓存流程
Java
1. 创建 ServiceA
├─ 实例化 ServiceA(构造函数)
├─ 暴露到三级缓存:singletonFactories.put(A, () -> getEarlyBeanReference(A))
├─ 属性注入:发现需要 ServiceB
↓
2. 创建 ServiceB
├─ 实例化 ServiceB
├─ 暴露到三级缓存
├─ 属性注入:发现需要 ServiceA
├─ 从三级缓存获取 ServiceA → 提升到二级缓存
├─ ServiceB 完成,放入一级缓存
↓
3. ServiceA 属性注入完成
├─ ServiceA 完成,放入一级缓存
├─ 清理二级、三级缓存
源码实现:getSingleton()
Java
public Object getSingleton(String beanName) {
// 1. 一级缓存查找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 2. 二级缓存查找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 3. 三级缓存查找
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 提升到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 清理三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
暴露早期引用:addSingletonFactory()
Java
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 放入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 移除二级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
AOP 代理处理
Java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
// 如果有 SmartInstantiationAwareBeanPostProcessor(如 AOP)
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
exposedObject = ((SmartInstantiationAwareBeanPostProcessor) bp)
.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
构造器循环依赖
问题示例
Java
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
报错:BeanCurrentlyInCreationException - 无法解决构造器循环依赖
解决方案:@Lazy
Java
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB; // 注入代理对象,延迟加载
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(@Lazy ServiceA serviceA) {
this.serviceA = serviceA;
}
}
@Lazy 原理
Java
// ContextAnnotationAutowireCandidateResolver 解析 @Lazy
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
if (isLazy(descriptor)) {
// 创建代理对象,首次使用时才真正获取 Bean
return buildLazyResolutionProxy(descriptor, beanName);
}
return null;
}
Prototype 循环依赖
为什么无法解决
Java
// Prototype Bean 不缓存,每次获取都创建新实例
@Service
@Scope("prototype")
public class PrototypeA {
@Autowired
private PrototypeB prototypeB;
}
@Service
@Scope("prototype")
public class PrototypeB {
@Autowired
private PrototypeA prototypeA;
}
报错:BeanCurrentlyInCreationException - Prototype Bean 不进入三级缓存
解决方案
Java
@Service
@Scope("prototype")
public class PrototypeA {
private PrototypeB prototypeB;
@Autowired
private ObjectProvider<PrototypeB> prototypeBProvider;
public void doSomething() {
// 延迟获取,每次调用获取新实例
this.prototypeB = prototypeBProvider.getObject();
}
}
循环依赖调试
查看正在创建的 Bean
YAML
// DefaultSingletonBeanRegistry
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
检查循环依赖配置
text
spring:
main:
allow-circular-references: true # Spring Boot 2.6+ 需显式开启
要点总结
| 要点 | 说明 |
|---|---|
| 三级缓存 | singletonObjects → earlySingletonObjects → singletonFactories |
| 解决条件 | 单例 + Setter/字段注入 |
| 构造器循环 | 不支持,需用 @Lazy |
| Prototype循环 | 不支持,需用 ObjectProvider |
| AOP影响 | 三级缓存提前生成代理 |
| 二级缓存意义 | 避免重复执行 singletonFactory.getObject() |
📝 发现内容有误?点击此处直接编辑