Spring中的循环引用,具有复合模式

时间:2011-12-20 23:17:39

标签: java spring design-patterns composite

让我说我有这样的结构: Composite Pattern

正如大多数人可能看到的那样,它是一种复合模式。如何使用Spring实例化这个模式?例如,如果我有以下代码:

<bean id="leaf1">
   <constructor-arg name="Name" value="leaf1" />
</bean>
 <bean id="leaf2">
   <constructor-arg name="Name" value="leaf2" />
</bean>
<bean id="leaf3">
   <constructor-arg name="Name" value="leaf3" />
</bean>
<bean id="composite1">
    <constructor-arg>
        <set>
            <ref bean="composite2" />
            <ref bean="leaf2" />
        </set>
    </constructor-arg>
</bean>
<bean id="composite2">
    <constructor-arg>
        <set>
            <ref bean="leaf3" />
                         <ref bean="leaf1" /> 
        </set>
    </constructor-arg>
</bean>

正如您在此处看到的,此配置将引发org.springframework.beans.factory.BeanCreationException,因为存在循环引用。复合包含Component列表,组件由Leaf和Composite组成。如何使用Spring解决这个问题?

2 个答案:

答案 0 :(得分:3)

这类问题的答案不是特定于Spring的,如果你使用常规构造函数在直接java中对此进行编码,则是相同的。

一个简单的解决方案是允许使用setter注入来配置复合,以便它打破构造中所需的引用循环,但这会降低构造函数注入理想主义。另一种方法是创建一个Composite的子类,该子类委托给另一个Composite。这将允许您稍后填写实际引用,虽然不是理想的,但您可以使用spring @postContstruct方法来检查引用是否确实已设置,这提供了与使用常规构造函数类似的安全措施。

有一些框架可以通过注入代理来自动处理这个循环构造函数的情况(例如nanocontainer),但这实际上只是隐藏了具有瞬态清新剂的设计气味。最终,你需要两阶段构造来实现这一点 - 在java中使用纯构造函数是不可能的,因为打破依赖循环需要使用非构造函数初始化器。

为这些类一起编写功能测试可能有助于从另一个角度强调设计问题。

答案 1 :(得分:1)

我认为它应该是复合IS-A组件,不是由Leaf和Composite组成的。 Composite应该是组件实例的集合。

public interface Component {
    void operation();
}

public class Leaf implements Component {
    public void operation() { System.out.println("I'm a Leaf"); }
}

public class Composite implements Component {
    private Collection<Component> components;

    public Composite() {
        this(new ArrayList<Component>());
    }

    public Composite(Collection<Component> components) {
        this.components =  = new ArrayList<Component>(components);
    }

    public void operation() { 
        System.out.println("I'm a Composite"); 
        for (Component component : this.components) {
            component.operation();
        }
    }
    public void addComponent(Component c) { this.components.add(c); }
    public void removeComponent(Component c) { this.components.remove(c); }
}