结论:工厂Bean与普通Bean相互依赖时候则必须先实例化普通bean,这是因为工厂Bean的特殊性,也就是其有个getObject方法的缘故。
测试代码:
public class MyFactoryBean implements FactoryBean,InitializingBean{
private String name;
private Test test;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DependentBean getDepentBean() {
return depentBean;
}
public void setDepentBean(DependentBean depentBean) {
this.depentBean = depentBean;
}
private DependentBean depentBean;
public Object getObject() throws Exception {
return test;
}
public Class getObjectType() {
// TODO Auto-generated method stub
return Test.class;
}
public boolean isSingleton() {
// TODO Auto-generated method stub
return true;
}
public void afterPropertiesSet() throws Exception {
System.out.println("name:" + this.name);
test = new Test();
test.name = depentBean.doSomething() + this.name;
}
}
public class Test {
public String name;
}
public class DependentBean {
@Autowired
private Test test;
public String doSomething(){
return "hello:";
}
}
xml配置:
<bean id="test" class="com.alibaba.test.circle.MyFactoryBean">
<property name="depentBean">
<bean class="com.alibaba.test.circle.DependentBean"></bean>
</property>
<property name="name" value="zlx"></property>
</bean>
其中工厂Bean MyFactoryBean作用是对Test类的包装,首先对MyFactoryBean设置属性,然后在MyFactoryBean的afterPropertiesSet
方法中创建一个Test实例,并且设置test,实例化MyFactoryBean最终会调用getObject
方法返回创建的Test对象。这里MyFactoryBean依赖了DepentBean,而depentBean本身有依赖了Test,所以这是个循环依赖。
测试:
public class TestCircle2 {
private final static ClassPathXmlApplicationContext moduleContext;
private static Test test;
static {
moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"});
test = (Test) moduleContext.getBean("test");
}
public static void main(String[] args) {
System.out.println(test.name);
}
}
运行会抛出org.springframework.beans.factory.BeanCreationException
。
分析原因:
当实例化test时候会触发getBean(“test”),会看当前bean是否存在,不存在则通过MyFactoryBean实例在创建。
创建MyFactoryBean时,实例化后放在singletonFactories中,然后对MyFactoryBean实例进行属性注入depentBean,属性注入时候会getBean(“depentBean”),发现depentBean不存在,就会实例化depentBean,然后放入singletonFactories,然后对depentBean进行autowired注入test,这时触发getBean(“test”),会到getSingleton返回实例化的test,即MyFactoryBean实例。由于test是工厂bean,所以返回test.getObject();而MyFactoryBean的afterPropertiesSet还没被调用,所以test.getObject()返回null。
列一下Spring bean创建的流程:getBean()->创建实例->autowired->set属性->afterPropertiesSet,也就是调用getObject方法早于afterPropertiesSet方法被调用了。
那么修改下MyFactoryBean为如下:
public Object getObject() throws Exception {
// TODO Auto-generated method stub
if(null == test){
afterPropertiesSet();
}
return test;
}
public void afterPropertiesSet() throws Exception {
if(null == test){
System.out.println("name:" + this.name);
test = new Test();
test.name = depentBean.doSomething() + this.name;
}
}
也就是getObject内部先判断,如果test==null那调用下afterPropertiesSet,然后afterPropertiesSet内部如果test==null在创建Test实例,看起来貌似不错,好像可以解决问题。但是实际上还是不行的,因为afterPropertiesSet内部使用了depentBean,而此时depentBean=null。
思考如何解决:
分析了原因是先创建了MyFactoryBean,并在创建MyFactoryBean的过程中又创建了DepentBean,而创建DepentBean时候需要autowired Test的实例,又需要MyFactoryBean,然后在调用afterPropertiesSet前调用getObject方法所以返回null。
那如果先创建DepentBean,然后在创建MyFactoryBean呢?
下面分析下过程:首先会实例化DepentBean,并且加入到singletonFactories,DepentBean实例会autowired Test,所以会通过MyFactoryBean创建Test实例。MyFactoryBean实例创建时会属性注入DepentBean实例,所以会调用getBean(“depentBean”),此时singletonFactories中已经有depentBean了,则返回depentBean对象,MyFactoryBean实例会属性注入DepentBean实例成功,Test实例初始化OK。也就是说DepentBean实例会autowired Test实例OK。
按照这分析先创建DepentBean,然后在实例化MyFactoryBean是可行的,修改xml为如下:
<bean id="dependentBean" class="com.alibaba.test.circle.DependentBean"></bean>
<bean id="test" class="com.alibaba.test.circle.MyFactoryBean">
<property name="depentBean">
<ref bean="dependentBean" />
</property>
<property name="name" value="zlx"></property>
</bean>
运行结果是没问题。