Digester 3在创建对象时调用构造函数两次
在Digester中存在一种奇怪的行为,我无法包裹头部。Digester 3在创建对象时调用构造函数两次
我有下面的代码调用时,它遇到“角色/角色”,在输入XML节点“角色”对象的构造函数:
AbstractRulesModule loader = (new AbstractRulesModule() {
protected void configure() {
forPattern("roles/role").createObject().ofType(Role.class)
.usingConstructor(String.class, String.class).then()
.callParam().fromAttribute("machine").ofIndex(0);
forPattern("roles/role").callParam().fromAttribute("name")
.ofIndex(1);
forPattern("roles/role").setNext("add");
}
});
Digester digester = DigesterLoader.newLoader(loader).newDigester();
List<Role> roles = new ArrayList<>();
digester.push(roles);
digester.parse(new File("c:/RoleMapping.xml"));
System.out.println(roles);
System.out.println(Role.count);
每一次角色的构造函数被调用,Role.count递增。奇怪的是,在针对以下xml运行上述代码之后,Role.count是2而不是1.当我调试代码时,Digester似乎试图创建2个额外的带有“null”作为构造参数的对象。
<roles>
<role name="m1" machine="mymachine" />
</roles>
这将导致各种问题,如果我有代码检查,如果构造函数的参数为null。
我的角色类的定义是:
public class Role {
private String machine;
private String name;
static int count = 0;
public Role(String machine, String name) {
this.machine = machine;
this.name = name;
count++;
}
}
我看到的问题是,3岁,但我最近碰到同样的事情来了,答案仍然是有效的...
的因为构造函数被调用两次是因为Digester 3使用参数处理构造函数。 Digester的问题是鸡和蛋......它不能调用构造函数直到它具有所有必需的参数,但因为规则可以从子元素获取它们的数据,所以它不具有所有子元素,直到它具有完全处理元素。
在你的情况,所有的参数都在属性可用,但考虑,如果你改变了你的XML:
<roles>
<role>
<name>m1</name>
<machine>mymachine</machine>
</role>
</roles>
甚至:
<roles>
<role>
<name>m1</name>
<machine>mymachine</machine>
<another>
<tag>which</tag>
<does>morestuff</does>
...
</another>
</role>
</roles>
消化器有效必须记住一切发生在<role>
和</role>
之间,因为可以在子数据中的任何位置调用调用参数规则,并且必须在创建对象之前完成所有这些操作。
要做到这一点,沼气工程创建一个围绕要构造的类的代理包装(Role),创建一个为所有构造函数参数传递null的虚拟实例,然后调用为主要元素的子项触发的所有其他方法。代理类拦截这些方法调用,记录它们(包括参数),并将它们传递给虚拟实例。一旦达到了end元素标签,虚拟对象就会被丢弃,一个新的构造函数参数被创建,并且所有记录的方法调用都被“重放”回新对象。你已经注意到,这不仅创建了两次对象,而且还调用了由消化器规则两次触发的所有方法:一次在“录制”阶段,一次在“回放”阶段。
这对所有简单的数据对象都适用,但在构建更复杂的数据对象时可能会产生奇怪的后果。一个例子见this digester ticket。
为了只是避免空指针异常,你可以告诉哪些值使用usingDefaultConstructorArguments
规则使用默认的构造函数参数沼气池:
forPattern("roles/role").createObject().ofType(Role.class)
.usingConstructor(String.class, String.class).then()
.usingDefaultConstructorArguments("one", "two").then()
.callParam().fromAttribute("machine").ofIndex(0);
对于更复杂的情况下,或只是如果你喜欢的方式,你可以使用构建器类和自定义规则。基本思想是当你到达将构建器类推入堆栈的元素以及元素结束标记上触发的自定义规则时。在处理元素体时,消化器将所有规则调用为正常将数据传递给构建器类。在结束标记处,会触发自定义规则,调用构建器来构建对象,然后用构建的对象替换蒸煮器堆栈上的构建器对象。这确实需要一个自定义的构建器类,但比听起来简单得多。有关工作示例,请参见this digester ticket。
希望这清除了神秘!
我也注意到这与消化器3.2的行为,同时试图绕过一些问题与我的对象构造函数beeing只调用null作为参数。你有没有发现是否调用两次是正常的?你有没有问题与你的构造函数调用错误的参数? – gsnerf 2013-10-29 13:28:32