依赖注入&循环依赖
依赖注入
依赖注入有2种方式:
Constructor-based dependency injection 和 Setter-based dependency injection
构造器注入例子:
SimpleMovieLister依赖了MovieFinder,然后通过构造参数来实现依赖注入
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private final MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
Setter注入的例子:
是在调用无参构造之后调用setter来注入的。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
自动注入
Spring自动注入的5种模式:
-
0:no不会自动注入 默认是这种
-
1:byName 根据bean的名字来装配
-
2:byType 根据bean的类型来装配
-
3:constructor 通过构造
-
4:auto-detect 我测试结果是有setter先用setter,没有就用constructor
Spring默认给Bean设置的自动注入默认为0,不自动注入。我们通过设置beanDefinition的AutoWireMode来改变自动注入的模式(虽然还可以在xml里设置)。
byName和byType都是通过setter来自动注入属性的,注意的是byName的setter的方法名需要为set属性名()
;而byType是不需要,是根据参数类型来判定的。
construtor是通过参数只有依赖属性的构造方法来注入的。
@Autowired来注入
使用@Autowired其实就是手动注入了,Spring默认为没有自动注入的时候我们是通过这种注解来注入依赖的。其实这种方式既不是通过setter也不是通过constructor,而是Spring会帮你去遍历这个类的所有Filed,把加了
@Autowire注解的
Filed`创建对应对象,并设置对象来注入的。
循环依赖
⚠️只有单例Bean才可以解决循环依赖!
Spring是如何解决循环依赖的呢?
三级缓存
Spring提供了三级缓存分别是:
- singletonObjects:单例池,存放已经创建完成,属性也填充好的对象。
- earlySingletonObjects:存放已经创建完成,但是属性没有注入好的对象。
- singletonFactories:存放已经创建完成,但是属性还注入好的对象的工厂对象,通过这个工厂可以返回这个对象。
使用三级缓存的原因
使用第三级缓存singletonFactories
的原因:因为Spring的AOP是在bean初始化之后的后置处理方法里实现的。如果没有第三级缓存,我们只能在实例化bean的时候就对其AOP代理了,或者就直接实例化,无法注入一个AOP的bean了。
使用这个存放工厂对象的缓存,咱们并没有真的注入一个bean,而是先注入一个半成品bean,在生命周期后面对用后置处理SmartInstantiationAwareBeanPostProcessor
方法完成处理AOP,就返回一个Aop处理后的对象,如果该类没有被代理就返回一个传入的bean。
循环依赖的流程
源码分析
当需要拿到一个单例bean是做了哪些判断?
DefaultSingletonBeanRegistry.java 里
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
//从一级缓存拿不到单例对象,然后这个单例对象正在创建,singletonCurrentlyInCreation是一个集合,记录了正在创建的bean的名字
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//从二级缓存中取
singletonObject = this.earlySingletonObjects.get(beanName);
//如果二级缓存中没有,是否允许早期引用(其实就是说要不要在三级缓存中找)
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
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;
为循环依赖做准备
通过lambda表达式创建工厂对象 - “a”,并放到三级缓存singletonFactories中
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
接下来填充属性populateBean(beanName, mbd, instanceWrapper);
在postProcessProperties
里有方法metadata.inject(bean, beanName, pvs);
会进行属性注入,B也是在这里通过反射fied
对象注入的。最后会去到Spring容器中找:beanFactory.getBean();
,实际上回去Spring到单例池singletonObjects(一级缓存)找。
然后肯定是找不到B的,又需要去创建bean B,走B的生命周期创建了,也会去为B创建一个工厂对象放到三级缓存中,然后B又需要属性填充了,B又依赖A,所以需要到Spring容器(单例池)中找A,找不到,会通过一级一级找下去,然后在三级缓存中找到。创建A后放到二级缓存中,依赖注入,然后B的生命周期继续走,走完后放到单例池里,从三级缓存的删除掉。
然后回到A的getBean(B),从单例池中获得B,依赖注入,A生命周期继续走,放到单例池中,从二级缓存中删掉。
最后注册到单例池的方法如下:
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);
}
}