一、DI和IOC的概念
DI和IOC从本质上是从不同角度描述了同一件事情:
- IOC:将对象的控制权交付给框架,由框架管理对象的生命周期,用户使用对象的时候直接从容器按照名称拿即可;(从容器的角度)
- DI:程序通过容器使用对象,在使用容器前需要注入相应的对象名称。(从程序的角度)
从我们开发的使用过程中,通常就是去容器中拿对应的对象,那么这个过程是怎么样的呢?本文将逐步分析IOC的整个流程,其中着重分析了:
- 多级缓存;
- 依赖注入的源码;
- 循环依赖解决。
二、spring容器缓存
在Spring默认的单例实例注册实现类(DefaultSingletonBeanRegistry)中,有多个Map存储不同时期的对象实例:
2.1 singletonObjects
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
在spring容器初始化后,所有的对象都会放入该缓存中。其结构是Map,即beanName为key,Instance为value。
2.2 singletonFactories
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
存储了多个beanFactory,每个bean有自己对应的beanFactory,这是所谓的二级缓存,该缓存的作用是,当程序在任何地方都找不到该bean对应的实例,此时会调用beanFactory来获取bean实例,并放入earlySingletonObjects。因此,有两个很重要的结论:
- earlySingletonObjects 中的bean都是由singletonFactories中的beanName根据beanFactor来创建放入;
- 二者的关系是此消彼长,earlySingletonObjects 拿出一个beanFactory,创建对象放入earlySingletonObjects 时就会从自身中把这个bean删除掉。
2.3 earlySingletonObjects
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
用于解决循环依赖问题,该缓存中存储的是半成品实例。 即这些实例在创建时由于其属性中有其他bean的引用,而引用的对象尚未创建(有依赖),此时需要暂停创建当前对象而且创建引用对象(依赖的对象)。此时当前的对象会放到earlySingletonObjects 中(通过singletonFactories ),提前暴露出来(解决循环依赖)。
2.4 registeredSingletons
将bean注册为单例且完整。(这部分我不是很理解)
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
addSingleton(beanName, singletonObject);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
2.5 singletonsCurrentlyInCreation
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
在spring容器创建一个bean时,会首先将beanName 放入该缓存,表示该bean正在被创建。 同理,当创建完成后,也会将该beanName移除该容器。
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
三、 spring容器启动过程(默认是单例实例)
- 声明该对象正在创建
- 加载配置文件,创建beanFactory;
- 获取配置文件中的对于Bean的definition,以便于构造对象和注入属性;
- fresh beanFactory,同时初始化bean实例(单例);
- 注册所有的单例bean并返回可用的容器引用。
3.1 bean的创建
大致流程如下: 将当前的beanName标记为正在创建;
- 使用initializeBean方法初始化实例(此时只有一个对象,没有属性注入);
- 如果支持循环依赖则生成三级缓存,并且尝试提前暴露(如果此时该beanName实例化完成,那么则不会加入缓存)
- 填充bean属性,初始化bean,这里可能会出现循环依赖从而引发别的bean的实例化,但是此时当前的bean已经放入beanFactories中了;
- 处理循环依赖,这里需要注意对于动态代理中的循环依赖的处理。在之后会单独讲解。
- 此时bean已经可以被使用,并尝试注册bean;
- 将bean放入容器中(一级缓存),标记已创建完成并删除二三级缓存的内容。
AbstractAutowireCapableBeanFactory中的createBean方法(做了一些安全性的保证)调用了doCreateBean方法(实际创建的方法):
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
......
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
.......
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
.......
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
.......
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
}
}
}
}
......
return exposedObject;
}
3.2 getSingleton的代码逻辑:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
四、获取bean的过程
4.1 getBean(AbstractApplicationContext)
调用BeanFactory 的getBean方法。
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
4.2 调用doGetBean
String beanName = transformedBeanName(name);
Object sharedInstance = getSingleton(beanName);
4.3 调用getSingleton(DefaultSingletonBeanRegistry)
该方法会返回注册的单例对象,并且检查安全性:
- 是否是已经创建好的对象;
- 对当前创建的单例的早期引用(解决循环引用)。
五、循环依赖解决
5.1 常规场景
说明一个场景:A与B之间存在循环依赖:
5.2 针对AOP过程中出现的循环依赖
其主要的处理逻辑在于下面的这段代码:
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
为什么在获取到了earlySingletonReference (必定存在循环依赖)的时候还要exposedObject == bean?我的理解是: 在进行对象初始化时(exposedObject = initializeBean(xxxx)),如果需要进行aop的话会调用:
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
exposedObject 其实指向的是被代理后的对象,而bean引用指向的还是原始对象。
- 如果exposedObject == bean,说明此时没有发送AOP,可以直接返回;
- exposedObject != bean,说明发生了AOP行为,处理AOP中的循环依赖。下面会继续解释。
5.3 为什么要使用二三级缓存?
二级缓存:earlySingletonObjects; 三级缓存:singletonFactories。 二级缓存就是用来存储创建到一半的对象,这些对象无法直接使用,但是可以解决循环依赖这个过程,因为已经可以拿到引用了。 二级缓存中的对象由三级缓存中的BeanFactory中创建,也就是三级缓存中只是提供了创建对象的方法。这样的好处是:
- 降低了没有必要的对象创建的开销;
- 将对bean实例的依赖转化成了对beanFactory的依赖,这样做的好处是可以解决动态代理的过程中出现的循环依赖。
如果两个对象A,B都需要AOP且存在循环依赖,那么过程是先创建A,在创建A的时候触发创建B,B创建并要获取A代理后的对象,此时会去一二级缓存中发现没有,此时再去singletonFactories创建早期对象,即调用getEarlyBeanReference,此时会检查对象是否需要被代理:hasInstantiationAwareBeanPostProcessors。如果需要就提前生成代理后的对象并放入二级缓存中。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
|