【问题标题】:using Spring, how to add items to a final, prepopulated list?使用 Spring,如何将项目添加到最终的预填充列表中?
【发布时间】:2015-09-18 04:46:12
【问题描述】:

鉴于这个类:

public class Node {
  private final List<Node> children;
  public Node(){
    children = new ArrayList<>();
    // possibly pre-populate the list here
  }
  public List<Node> getChildren(){
    return children;
  }
}

在 Spring 中创建带有子节点的节点的最佳方式是什么,其中一些子节点是由类定义的,而一些子节点应该是可配置的?

<bean id="node1" class="Node">
  <property name="children">
    <add-item><bean class="Node"/></add-item> <!--pseudocode-->
    <add-item><bean class="Node"/></add-item> <!--pseudocode-->
  </property>
</bean>

我在 Spring IoC 文档和论坛中找到的答案并没有让我满意,因为提议的解决方案涉及不同程度的开销:附加映射 bean、工厂方法、要继承的抽象 bean,……难道没有更多的东西优雅吗?

【问题讨论】:

标签: java spring


【解决方案1】:

我会直接将列表注入构造函数。这使得使用 final 字段变得不难:

public class Node {
  private final List<Node> children;
  public Node(List<Node> children){
    this.children = children;
  }
  public List<Node> getChildren(){
    return children;
  }
}

<bean id="node1" class="Node">
  <constructor-arg>
        <list>
            <item><bean class="Node"/></item> <!--pseudocode-->
            ...
        </list>
    </constructor-arg>    
</beans>

编辑您的评论:

如果你想在之前或之后进行任何初始化,你可以简单地这样做:

...
public Node(List<Node> children){
    this.children = new ArrayList<>();
    // do some initialization before...
    this.children.addAll(children);
    // do some initialization after...
}
...

【讨论】:

  • 是的,很好的答案,但是,如果我在课堂上有预定义的项目怎么办? (编辑我的例子来澄清)
  • 好吧,我可以让构造函数在预填充的项目之后添加提供的项目,这样就可以了
【解决方案2】:

我稍微改变了 Node 类

public class Node implements InitializingBean{

private final List<Node> children;

private String name;

public Node() {
    children = new ArrayList<>();
}

@Override
public void afterPropertiesSet() throws Exception {
    //predefined nodes
    Node node = new Node();
    node.setName(":)");
    children.add(node);
}

public String toString(){ return name; }

public List<Node> getChildren() {
    return children;
}

public void setNodes(Node nodes[]){
    if(nodes != null){
        for(Node node: nodes){
            this.children.add(node);
        }
    }
}

public void setChildren(Object something){
    System.out.println(something.getClass());
}
public void setName(String name) { this.name = name; }

}

诀窍是迭代参数值并添加到预定义的数组列表中。 之所以我将其命名为setNodes 而不是setChildren,是因为Spring 会从getChildren 内省children 属性的类型。如果我将 setter 命名为setChildren,我将得到IllegalArgumentException

当然,我可以使用ArrayList 代替本机数组,但您说您不想要任何开销。创建数组没什么,将数组中的对象添加到ArrayList 只是创建一个便宜的引用。

这是 bean 定义

<bean id="node1" class="playground.Node">
    <property name="nodes">
        <array >
            <bean class="playground.Node">
                <property name="name" value="A" />
            </bean>
            <bean class="playground.Node">
                <property name="name" value="B" />
            </bean>
        </array>
    </property>
</bean>

【讨论】:

  • 是的,我还想到了一个列表属性,可读,代表所有孩子,第二个列表属性,可写,添加孩子。它确实有效并且还不错,但仍然感觉像是“使域模型适应 Spring 功能”而不是“使用 Spring 适应域模型”
  • 如果你根本不想修改模型类,你应该改用 Spring java config。另一种解决方法是使用MethodInvokingFactoryBean 调用add 方法,但除非Spring XML 支持循环,否则它非常麻烦且一点也不优雅。
【解决方案3】:

您没有指定孩子的来源。作为第二种选择,如果您不想将 List 注入构造函数,则可以使用 @PostContruct 方法:

public class Node {
  private final List<Node> children;
  public Node(){
    children = new ArrayList<>();
  }
  public List<Node> getChildren(){
    return children;
  }

  @PostConstruct
  public void init() {
    // this method will Spring call after dependency injection is done.
    children.add(...)
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-16
    • 2021-05-29
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 2021-04-09
    • 1970-01-01
    相关资源
    最近更新 更多