工厂Bean与普通Bean循环依赖

Spring&SpringBoot 专栏收录该内容
49 篇文章 0 订阅

转载自:
http://ifeve.com/%E8%AE%BAspring%E4%B8%AD%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%9A%84%E6%AD%A3%E7%A1%AE%E6%80%A7%E4%B8%8Ebean%E6%B3%A8%E5%85%A5%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%85%B3%E7%B3%BB/

  结论:工厂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>

运行结果是没问题。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值