Digester有一种奇怪的行为,我无法绕过头。
我有以下代码,只要遇到输入xml中的“roles / role”节点,就会调用“Role”对象的构造函数:
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的构造函数时,Role.count都会递增。奇怪的是,在针对以下xml运行上面的代码之后,Role.count是2而不是1.当我调试代码时,似乎Digester试图用“null”作为构造函数参数创建2个额外的对象。
<roles>
<role name="m1" machine="mymachine" />
</roles>
如果我检查构造函数的参数是否为null,这会导致各种问题。
我的Role类的定义是:
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++;
}
}
答案 0 :(得分:0)
我看到这个问题是3岁了,但我最近遇到了同样的事情,答案仍然有效......
构造函数被调用两次的原因是Digester 3处理带参数的构造函数的方式。 Digester的问题是鸡和鸡蛋......在它具有所有必需参数之前它不能调用构造函数,但因为callParam
规则可以从子元素获取它们的数据,所以它没有所有子元素元素直到它完全处理了元素。
在您的情况下,属性中提供了所有参数,但请考虑您是否将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的虚拟实例,然后调用为主元素的子元素触发的所有其他方法。代理类拦截这些方法调用,记录它们(包括参数),并将它们传递给虚拟实例。一旦到达结束元素标记,就丢弃虚拟对象,用真实构造函数参数创建一个新对象,并且所有记录的方法调用都被重放&#39;回到新对象。
正如您所注意到的,这不仅会创建对象两次,而且还会将消化器规则触发的所有方法调用两次:一次在录制过程中。阶段,以及“播放”期间的一次相。
这一切都适用于简单的数据对象,但在构造更复杂的数据对象时可能会产生奇怪的后果。有关示例,请参阅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。
希望这能揭开神秘面纱!