一次错误DI的思考
问题引出
在某一天的开发中,我突然发现有“神人”将controller的Bean注入到了service中,为的是调用controller中的一个方法,同时还在那个controller中注入了service,还运行的没一点毛病。按照正常的Bean加载过程,这应该造成了循环依赖,但是为什么运行没有问题呢?Spring是如何解决的呢?
依赖注入
依赖注入是一种设计模式,它将对象之间的依赖关系从代码中移除,并由容器来管理这些依赖关系。依赖注入的主要目的是降低代码耦合度。
Spring的依赖注入通过IOC容器实现。在Spring中,IOC容器负责创建和管理Bean对象,以及管理对象之间的依赖关系。
在Spring中,依赖注入通过反射机制来实现的,当IOC容器实例化Bean的时候,会检查Bean所声明的依赖关系,并尝试通过反射来注入这些依赖关系。依赖注入通常分为三种方式:构造函数注入、setter方法注入和字段注入(注解)。
那么按照问题所说的,直白来看,就相当于:
创建A对象时,解析填充A对象的属性,发现依赖的B对象未创建,则触发B对象创建;创建B对象时,发现其依赖A对象,这就产生了依赖冲突,类似于死锁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(new A());
}
}
class A {
public A() {
new B();
}
}
class B {
public B() {
new A();
}
}
那么SpringBoot是如何解决的呢?
循环依赖场景
大部分的开发者应该都写过类似的代码:
1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class AServiceImpl implements AService {
@Autowired
private BService bService;
...
}
@Service
public class BServiceImpl implements BService {
@Autowired
private AService aService;
...
}
这就是经典的循环依赖场景,显然Spring在底层解决了这种问题,
那按照依赖注入的三种方式来看看每个场景
构造函数注入
1 2 3 4 5 6 7 8 9 10
@Service public class AServiceImpl implements AService { public AServiceImpl(BServiceImpl b) { } } @Service public class BServiceImpl implements BService { public BServiceImpl(AServiceImpl a) { } }
这种注入方式会抛出异常:
BeanCurrentlyInCreationException1 2 3 4 5
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
构造器注入构成的循环依赖是无法解决的,这也是构造器注入最大的劣势。
其根本原因是,Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是==已实例化==,但还==未初始化==的状态(内存已经分配,但是还未执行实例初始化)。而构造器是完成实例化时调用,因此构造器注入的循环依赖无法解决。
setter方法注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@Service public class AServiceImpl implements AService { private BService bService; @Autowired public void setBService(Bservice b) { this.bService = b; } } @Service public class BServiceImpl implements BService { private AService aService; @Autowired public void setAService(Aservice a) { this.aService = a; } }
字段注入
1 2 3 4 5 6 7 8 9 10 11 12
@Service public class AServiceImpl implements AService { @Autowired private BService bService; ... } @Service public class BServiceImpl implements BService { @Autowired private AService aService; ... }
Spring解决循环依赖的原理
Spring的循环依赖的理论依据基于Java的“引用传递”(java只有值传递),当获取对象的引用时,对象的属性是可以延后设置的。(但是构造器必须在获取引用之前,因为分配内存后,需要调用构造函数才能初始化引用)
Bean创建的流程
createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象。populateBean:填充属性,这一步主要是对bean的依赖属性进行注入initializeBean:回到一些如initMethod、InitializeingBean等方法
从对单例Bean的初始化流程可以知道,循环依赖主要出现在populateBean()方法这里。
三级缓存
在Spring容器的整个声明周期中,单例Bean只有一个对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
// springboot3 添加的
final Lock singletonLock = new ReentrantLock();
// 单例Bean对象的缓存,一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 单例Bean工厂的缓存,三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap(16);
// 早期单例Bean对象的缓存(尚未填充属性),二级缓存
private final Map<String, Consumer<Object>> singletonCallbacks = new ConcurrentHashMap(16);
// 已注册的Bean缓存;单例Bean创建成功后的缓存
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
// 当前正在创建的Bean;表示Bean创建过程中,都会在这里缓存
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}
获取单例Bean的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Nullable
public Object getSingleton(String beanName) {
return this.getSingleton(beanName, true);
}
/**
* 返回给定名称注册的(原始)单例对象
* 检查已实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)
* beanName – 要查找的 bean 的名称 allowEarlyReference – 是否应创建早期引用
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取Bean
Object singletonObject = this.singletonObjects.get(beanName);
// 如果获取不到Bean对象并且Bean对象正在创建中
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存中获取Bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 若二级缓存也无法获取,并且允许创建早期引用(三级缓存)
if (singletonObject == null && allowEarlyReference) {
// 获取锁
if (!this.singletonLock.tryLock()) {
return null;
}
try {
// 进入同步代码块后,再次检查一、二级缓存
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 获取Bean工厂
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 将Bean从三级缓存移动至二级缓存
if (this.singletonFactories.remove(beanName) != null) {
this.earlySingletonObjects.put(beanName, singletonObject);
} else {
singletonObject = this.singletonObjects.get(beanName);
}
}
}
}
} finally {
this.singletonLock.unlock();
}
}
}
return singletonObject;
}
获取单例Bean时,会先从一级缓存、二级缓存中获取,若获取不到,则会利用三级缓存singletonFactories的getObject() 方法获取。
Bean对象添加至三级缓存的前提是执行了构造器,因此构造器的循环依赖无法解决。
从上面的源码可知,Spring解决循环依赖的核心是singletonFactories,缓存ObjectFactory
1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
经过ObjectFactory.getObject()后,此时放进了二级缓存earlySingletonObjects内。这个时候对象已经实例化了,虽然并不完善,但是对象的引用已经可以被其它引用了。
上面所讲的,二级缓存存储的是尚未填充属性的单例Bean对象,那么对于一个Bean来说,什么时候该进入二级缓存,什么时候该被移除二级缓存呢?
- 添加:向二级缓存存储Bean的地方只有
getSingleton(),方法,将Bean对象从三级缓存中移动 - 移除:
addSingleton()、addSingletonFactory()、removeSingleton(),添加Bean、添加Bean工厂的时候都会删除二级缓存中对应的缓存值。
源码解析
Spring IOC 容器会将每一个正在创建的Bean 标识符缓存在singletonsInLenientCreation中,可以看作==当前创建Bean池==,Bean在创建过程中会一直缓存在里面,直到创建完毕。
这时候在按照上面所说的Bean的创建流程,首先需要getBean()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 获取Bean的标识符
String beanName = transformedBeanName(name);
Object beanInstance;
// 检查单例缓存中是否存在手动注册的单例
Object sharedInstance = getSingleton(beanName);
......
// 如果不是只检查类型,那就将Bean缓存在当前创建Bean池
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
......
// Create bean instance.
if (mbd.isSingleton()) {
// 这个getSingleton()是DefaultSingletonBeanRegistry中的一个重载方法
// 这个方法会在singletonFactory.getObject() 执行的前后,分别执行beforeSingletonCreation(beanName)、afterSingletonCreation(beanName)
// 保证Bean在创建过程中,放入正在创建的缓存池中
sharedInstance = getSingleton(beanName, () -> {
try {
// 调用AbstractAutowireCapableBeanFactory的createBean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
}
在doGetBean()中会调用createBean()
1
流程总结
假设 AService 与 BService 形成了循环依赖
Spring 实例化、初始化 A 这个单例Bean。
AbstractBeanFactory.doGetBean("aService")1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// 已经创建过至少一次的 bean 的名称 private final Set<String> alreadyCreated = ConcurrentHashMap.newKeySet(256); @SuppressWarnings("unchecked") protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = transformedBeanName(name); Object beanInstance; // 检查单例缓存中是否存在手动注册的单例 // 此时 aService 不存在任何一级缓存中 Object sharedInstance = getSingleton(beanName); ...... // 在alreadyCreated中缓存Bean标识符 if (!typeCheckOnly) { markBeanAsCreated(beanName); } ...... // Create bean instance. if (mbd.isSingleton()) { // 通过调用createBean()来创建实例,标记aService正在创建 // 实例创建完成后,会移除正在创建Bean池,并且执行addSingleton() // 二、三级缓存执行remove,一级缓存执行add sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } }
执行
AbstractAutowireCapableBeanFactory.doCreateBean()1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } // 调用构造器 / 工厂方法 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // 此处的bean为“原始Bean”,也就是Bean实例对象 aService@1234 Object bean = instanceWrapper.getWrappedInstance(); ...... // 是否要提前暴露(循序循环依赖);此处AService是允许的 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // 允许暴露,就把AService 绑定在ObjectFactory上,注册到三级缓存singletonFactories // 执行getEarlyBeanReference(),创建自动代理对象(注意执行时机 为执行三级缓存的时候) if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. exposedObject为最终返回对象 Object exposedObject = bean; try { // 为 aService@1234 的属性进行赋值操作,@Autowired等注解在这里作用 // 因此循环依赖的 bService 会在此调用 getBean("bService"),创建实例 bService@56 // 在bService创建时,也会调用 getBean("aService"),最终调用getSingleton("..") // 此时上面的getEarlyBeanReference()会被执行,因此@Autowired注入的是一个代理对象 populateBean(beanName, mbd, instanceWrapper); // 实例化,这里会执行后置树立起BeanPostProcessor的两个方法 // exposedObject = initializeBean(beanName, exposedObject, mbd); // 至此,相当于aService@1234已经实例化完成、初始化完成(属性也赋值了) ...... } }
参考文章
https://cloud.tencent.com/developer/article/1497692
