示例:
@Component
@EnableTransactionManagement
public class CircularRefA{
@Autowired
CircularRefB circularRefB;
@Override
public String toString() {
return "CircularRefA";
}
@Transactional
public void test(){
System.out.println("------------------");
}
}
@Component
public class CircularRefB {
@Autowired
CircularRefA circularRefA;
@Override
public String toString() {
return "CircularRefB";
}
}
如上图这种,出现相互引用的情况,A在创建之后填充属性B,B在创建之后又要填充属性A,会导致A又创建…会一直循环下去,但实际情况却不是,看spring源码时如何解决的。
如果是先实例化CircularRefA,会触发CircularRefA的getBean操作
代码片段一:doGetBean()方法
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
.....
//从缓存中拿实例
Object sharedInstance = getSingleton(beanName);
.....
else {
//如果singletonObjects缓存里面没有,则走下来
//如果是scope 是Prototype的,校验是否有出现循环依赖,如果有则直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
.....
try {
//父子BeanDefinition合并
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
.....
//单例
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
//FactoryBean接口的调用入口
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
.....
}
}
.....
}
return (T) bean;
}
首先从缓存中拿CircularRefA实例,调用代码片段一中的getSingleton(beanName)方法。如下图所示:
假设spring作用域是单例的,会走到代码片段一如下图所示的部分:
看这个getSingleton的代码:
上图中的这个函数式接口,会进入createBean()方法中的doCreateBean()方法,返回创建好的实例,然后放入一级缓存中。
。
代码片段二: doCreateBean方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
.....
if (instanceWrapper == null) {
//创建实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//收集类中的注解@Resouce和@Autowired等等注解
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
//是否允许单例bean提前暴露
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));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//在这里进行依赖注入
populateBean(beanName, mbd, instanceWrapper);
.....
}
.....
return exposedObject;
}
在代码片段二中,判断是否允许bean提前暴露的条件是:
1、必须是单例
2、允许循环依赖
3、正在创建当中
如果条件满足,会添加一个三级缓存,为属性注入做铺垫,代码如图所示:
接下来在代码片段二中会调用 populateBean方法进行依赖注入CircularRefB,大致的流程如图所示:
又重新回到这里进行CircularRefB的实例化
这个流程跟CircularRefA的差不多,然后CircularRefB会进行属性装配CircularRefA,又重新回到下图所示进行CircularRefA的实例化。
还是先去缓存中查找CircularRefA的实例
三级缓存 singletonFactory.getObject() 函数式接口会走到getEarlyBeanReference()方法
因为CircularRefA中有@Transactional注解,所以最终会给CircularRefB注入一个CircularRefA的代理对象
这样就完成了CircularRefB的装配,接下来集合中删除表示正在创建的实例CircularRefB,并将CircularRefB添加到一级缓存当中,然后删除二级三级缓存。
CircularRefB实例化之后CircularRefA也就装配完成了,接下来的流程和CircularRefB的流程差不多,不再赘述。补充:多例实例不允许循环依赖,会直接报错,有兴趣的可自行查看源码。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13825.html