让我说我有这样的结构:
正如大多数人可能看到的那样,它是一种复合模式。如何使用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解决这个问题?
答案 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); }
}