一、bean的初始化回顾
二、bean的循环依赖示例
使用spring bean的循环依赖在我们开发中是很普遍的,并且spring容器中也默认支持循环依赖。以下为一个简单的bean的循环依赖示例:
@Component
public class UserService {
@Autowired
private TestService testService;
public UserService() {
System.out.println("userService初始化完毕");
}
}
@Component
public class TestService {
@Autowired
private UserService userService;
public TestService() {
System.out.println("testService创建结束");
}
}
UserService中注入了testService,TestService中注入了userService,形成了一个循环依赖。
三、testService的bean初始化过程回顾
bean的初始化顺序如上流程图所示,spring容器启动,根据指定包路径扫描路径下所有加了主键的类,将beanName加入到一个list中,对list进行遍历。此时会先对testService的bean进行初始化,因为根据排序值t>u,testService排在userSerivce前面。
testService不是抽象的类,并且是单例,非懒加载的类,因此会在spring容器启动时加载该类。进入核心方法doGetBean()中进行初始化。
· doGetBean()
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//根据testService作为beanName查询缓存池中是否存在bean,此时为null
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//...
} //testService的bean为null,进入else分支 else {
//...
try {
//...testService是否需要加载dependOn依赖的bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
//属于单例,testService的bean加载进入该分支
if (mbd.isSingleton()) { //获取bean sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try { //lambda——>创建bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} //...
}
doGetBean中可以看几个关键的流程,首先是查询缓存池判断是否存在testSerivce的Bean,此时的testService的bean肯定为null,并且testService为单例,所以进入判断单例的分支中进行该bean的创建。
· getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) { //判断一级缓存中是否存在bean Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) {
//这里有个细节,会把该bean放入到正在创建的集合中 //testSerivce此时作为正在创建的bean放入到集合中 beforeSingletonCreation(beanName);
//...
try { //这里会直接从testService bean的工厂取出bean对象 //这里的bean工厂的创建,其实就是在lmd中的createBean中完成 singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//...
}
getSingleton中出现了一段有意思的代码,就是会把正在创建的bean的name放入到集合中。这里的集合其实就是为了给循环依赖时候的判断所使用,说白了就是为了保证在对UserSerivce初始化时,对testSerivce进行注入时候可以知道testService处于正在创建时,无需进行其他创建之前的操作。
· createBean
在上述的getSingleton方法中,会根据singletonFactory.getObject()获取bean对象。其实这里的sigletonFactory是doGetBean中有段lmd表达式的createBean的运行结果。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
//...
// 从beanDefinition中取出该bean的class类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
//... 开始创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
· doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
//...
if (instanceWrapper == null) {//实例化对象instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//...}
在createBeanInstance中创建出了testService早期的对象,因为通过debug可以看到wrapper中testService的bean中依赖的userSerivce的bean还没有被注入。
运行到createBeanInstance后,没有执行bean的初始化方法。
继续执行代码:
//判断是否允许循环依赖//条件一:单例//条件二:私有属性默认为true,不过可以修改,私有属性在beanFactory中//条件三:是否处于正在创建队列中。在调用createBean方法之前,已经放在了创建队列中。boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
} //将bean生成的bean工厂放到二级缓存中 addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException { //这里主要判断是否存在aop,在后面的spring aop中会讲到,这里不做阐述
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
//...
到目前为止,都和非循环依赖的bean初始化过程是一致的,并且testService对应的bean工厂已经被放到了二级缓存中。
四、通过populateBean开始循环依赖注入
testService在对属性注入时,发现需要注入userSerivce的bean,就会根据userService的注解@Autowired选择对应的策略模式postProcessPropertyValues开始注入userService,直到底层仍调用了beanFactory.getBean(),开始重复执行bean初始化的过程。
userService bean初始化过程:getBean()—>doGetBean()—>getSingleton(beanName)校验userSerivce的bean是否存在一级缓存中,如果存在则返回,不存在继续初始化—>getSingleton(beanName,beanFactory)—>将userSerivce放入正在初始化的集合中—>createBean()—>doCreateBean()—>createBeanInstance()创建userService的早期对象—>将userSerivce的bean工厂放到二级缓存中—>populateBean—>开始依赖注入testService的bean。
此时又回到了对于testService的bean的初始化,getBean()—>doGetBean()—>getSingleton(beanName)。
· 通过getSingleton(testService)查找testService的bean
此时testSerivce的bean没有创建出来,我们观察下getSingleton中是如何找到testService的bean的。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //查找一级缓存中是否存在testSerivce的bean Object singletonObject = this.singletonObjects.get(beanName); //一级缓存中不存在,并且正在初始化的集合中存在testService
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) { //去三级缓存中查找是否存在 singletonObject = this.earlySingletonObjects.get(beanName);
//三级缓存中不存在,并且allowEarlyReference为true //allowEarlyReference因为上游传来就是一个写死的true if (singletonObject == null && allowEarlyReference) { //二级缓存中是否存在,存在就放到三级缓存中 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
(1)首先会从一级缓存中查找testService的bean,由于testService的bean进入了循环依赖中并没有完成创建出来所以这里肯定为null。
(2)一级缓存不存在bean并且在初始化集合中存在需要查询的bean,才会继续查找。通过之前的初始化过程我们也可以testService或者userSerivce已经被放入到了集合中。所以通过校验。
(3)去三级缓存中查找是否存在testService的早期bean,发现仍不存在为null,因为可以通过之前的初始化过程知道testService在doCreateBean时,被放入到了二级缓存中。
(4)去二级缓存中查询是否存在testService的工厂bean,发现存在二级缓存中,此时会把二级缓存放到三级缓存中,从二级缓存中删除这个工厂bean(为了方便gc所以需要删除)。
由此可以得出,正在初始化过程中的早期bean会存在二级缓存或三级缓存中,并且在二级缓存中的beanFactory,会被移到三级缓存中。为什么要这么处理,其实是和优化了一部分的性能。
当我们看下sigletonFactory.get(beanName)时,可以看到调用的底层方法是如下图所示的:
通过该bean工厂获取bean时,工厂会对bean所有的后置处理器进行遍历与操作该bean,或者如果实现了aop还会新生成一个代理对象。而在getSingleton方法中也可以看到是先取一级缓存,一级缓存不存在再取三级缓存,不直接取调用过程更复杂的二级缓存中的beanFactory。而且当三级缓存不存在,二级缓存存在时,会获取二级缓存中beanFactory中的bean放到三级缓存中,保证重复初始化bean的时候不再重复执行bean的后置处理器来提高性能。
到此为止,在初始化userService的bean时,所有的属性都注入完毕(即testService的bean被获取到),userService也初始化成功,并被放入到了一级缓存,从正在创建的集合中被移除。
//...finally { if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
} //从集合中移除userService afterSingletonCreation(beanName);
}
if (newSingleton) { //真正的userService的bean被创建出来了,放到一级缓存中 addSingleton(beanName, singletonObject);
}
userService的bean真正创建出来后,又回到最开始的testService的bean初始化中,testService内部属性也可以被注入成功(即userService的bean已经创建成功了)。循环依赖结束。
五、循环依赖的bean初始化过程-图
与普通bean的注入相比,会发现循环依赖的bean的注入,会直接从二级缓存或三级缓存中获取早期的bean,来提高bean注入的性能。
testService、userSerivce的bean依赖注入顺序:
(1)初始化testSerivce,生成了早期的testSerivce bean factory放到了二级缓存,通过populateBean注入userSerivce。
(2)开始初始化userSerivce,生成了早期的userService bean factory放到了二级缓存,通过populateBean需要注入testService。
(3)底层调用了getSingleton,从二级缓存池中获取testService的bean factory并根据工程获得bean实例,将testSerivce的bean放到了三级缓存,从二级缓存中删除bean factory(方便gc)。
(4)userService初始化完毕,放到一级缓存中,从三级缓存中删除bean。
(5)testService初始化完毕,放到一级缓存中,从三级缓存中删除bean。
六、如何关闭循环依赖
spring容器中是默认开启循环依赖的。
我们只需要修改他为false即可。修改方式:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
AbstractAutowireCapableBeanFactory beanFactory = (AbstractAutowireCapableBeanFactory) context.getBeanFactory();
beanFactory.setAllowCircularReferences(false);
context.register(AppConfig.class);
context.refresh();
}
循环依赖开启的参数是在AbstractAutowireCapableBeanFactory类中,所以我们通过获取spring容器中的beanFactory对象,通过他的子类来setAlloCircularRefereces(false)即可。
然后将spring容器手动进行启动。
运行结果,无法循环依赖注入:
七、为什么构造函数注入无法进行循环依赖
修改testSerivce类中注入UserService中的注入方式为构造函数注入,修改后代码如下:
@Component
public class TestService {
private UserService userService;
@Autowired
public TestService(UserService userService) {
this.userService = userService;
}
public TestService() {
System.out.println("testService创建结束");
}
}
运行结果:
根据报错信息可以追溯到底层仍调用了该方法,并且autowiredBeanName为userService:
return (instanceCandidate instanceof Class ?
descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
resolveCandidate进去后,会发现一段熟悉的代码:
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
//开始初始化userSerivce的bean
return beanFactory.getBean(beanName, requiredType);
}
此时testService由于构造函数没有执行完毕,所以testService的早期对象并没有被创建出来,并且在二级缓存或三级缓存中也没有testService的bean工厂和bean示例。因此在对userSerivce对象进行属性注入testService,由于找不到testService的bean而报错。