完全代码就不贴出来了。在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean中的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));这行代码中执行了 () -> getEarlyBeanReference(beanName, mbd, bean)存在后置处理器。此方法可以改变返回提前暴露的对象。通过addSingletonFactory方法将得到的需要暴露的对象添加到singletonFactories中以解决循环依赖。下面通过测试证明。
写2个简单的测试类。
package test.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import test.dao.Dao;
@Component
public class Test {
@Autowired
Dao dao;
}
package test.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import test.config.Test;
@Component
public class DaoImpl implements Dao {
@Autowired
Test test;
// public DaoImpl() {
// }
//
// public DaoImpl(String s) {
// }
@Override
public void query() {
System.out.println("DaoImpl");
}
}
此时DaoImpl依赖了Test,Test也依赖DaoImpl。存在循环依赖。可以知道这样程序运行是没有问题的。截图如下:
我们建一个测试类。
package test.mypostprocessors;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;
import test.dao.Dao;
import test.dao.DaoImpl;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
@Component
public class MYSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
//预测最终从这个处理器的回调返回的bean的类型。
// @Override
// public Class> predictBeanType(Class> beanClass, String beanName) throws BeansException {
//// return String.class;
// if(beanName.equals("daoImpl"))
// System.out.println("predictBeanType");
// return null;
// }
//第二次调用
//确定要用于给定bean的候选构造函数。
// @Override
// public Constructor>[] determineCandidateConstructors(Class> beanClass, String beanName) throws BeansException {
// if(beanName.equals("daoImpl")) {
// System.out.println("determineCandidateConstructors");
// Constructor>[] declaredConstructors = beanClass.getDeclaredConstructors();
// List> constructors = new ArrayList<>(declaredConstructors.length);
// for (java.lang.reflect.Constructor> declaredConstructor : declaredConstructors) {
// constructors.add(declaredConstructor);
// }
// List> constructor=new ArrayList<>();
// Constructor> temp = constructors.get(1);
// constructor.add(temp);
// return constructor.toArray(new Constructor>[0]);
//// Constructor>[] declaredConstructor =declaredConstructors[0];
// }
// return null;
// }
/**
*获取对指定bean的早期访问的引用,通常用于解析循环引用。
* 这个回调函数将会给后置处理器一个早点暴露一个wrapper的机会,也就是说,在目标的bean实例完全初始化之前。
*所暴露的对象在某些方面应该等价于postProcessBeforeInitialization和postProcessAfterInitialization这个对象。
* 注意被方法返回的对象将会被用于bean的引用除非从被定义的post-processor调用返回了不一样的wrapper,换句话说,
*这些post-processor调用可能最终使用了相同的引用或者从那些后来的回调中返回了原生的bean实例
* (如果受影响bean的wrapper已经为调用此方法而构建,默认情况下会被当成最终的暴露bean引用)
* @param bean the raw bean instance
* @param beanName the name of the bean
* @return
* @throws BeansException
*/
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
if(beanName.equals("test"))
System.out.println("getEarlyBeanReference");
return null;
}
}
原本getEarlyBeanReference是返回bean,现在我改为null,加个条件是为了证明出错的位置。
上图可以知道,出错是在打印之后。为什么会出错呢?
前面的文章提到过(循环依赖那一章)。这里贴下关键源码。
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);//AspectjAutoProxy
}
}
}
return exposedObject;
}
首先lamda表达式中返回的exposedObject通过addSingletonFactory传进去,然后this.singletonFactories.put(beanName, singletonFactory);添加到singletonFactories中,这个map非常重要,循环依赖中有说,由于我们返回了null,打破了循环依赖的规律,导致循环依赖有问题。所以会报错。
getEarlyBeanReference
三月 03, 2020 11:09:09 上午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'test': Unsatisfied dependency expressed through field 'dao'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'daoImpl': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'test': Requested bean is currently in creation: Is there an unresolvable circular reference?
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'test': Unsatisfied dependency expressed through field 'dao'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'daoImpl': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'test': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:415)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1434)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
at test.App.main(App.java:16)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'daoImpl': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'test': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:415)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1434)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1291)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1211)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:656)
... 13 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'test': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:342)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:218)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1291)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1211)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:656)
... 26 more
Process finished with exit code 1
错误如上,可以自己对比看一下。
我们将MYSmartInstantiationAwareBeanPostProcessor中的代码改成这样就可以了:
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
if(beanName.equals("test"))
System.out.println("getEarlyBeanReference-----"+beanName);
return bean;
}
打印getEarlyBeanReference-----test说明执行了这个方法,执行没有出错说明上面的错修改回来了。这个需要对照循环依赖的那篇文章一起看才行。
总之,我们自定义一个类实现SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanReference方法,这个方法就是解决循环依赖的bean,通过后置处理器返回bean,如果改变了返回,造成循环依赖不上就会出错。