这个问题不是在寻找问题的解决方案,而是在解释为什么会出现问题(或不会出现问题!)
Class.getMethods()
的Javadocs说:
返回的数组元素没有经过排序,也没有特定的顺序。
事情是这样的:我们使用一个叫做JMTE的简洁的Java模板库已经有几年了,没有任何问题。它使用类似jstl的语法将模型值注入模板中。特别是,我们一直在使用这样的东西来渲染价格:
${vendor.currency.symbol} ${order.amount}
第一个翻译成如下:
vendor.getCurrency().getSymbol()
其中getCurrency()
返回java.util.Currency对象。Currency有两种获取货币符号的方法——一种接受特定的Locale,另一种使用默认的。
public String getSymbol()
public String getSymbol(Locale locale)
在过去的18个月左右,一切都运行良好,货币代码/符号出现在电子邮件中。然后5天前,当我们试图替换${vendor.currency.symbol}
IllegalArgumentException
。经过一些调试,我找到了原因,在JMTE的深处:
for (Method method : declaredMethods) {
if (Modifier.isPublic(method.getModifiers())
&& (method.getName().equals("get" + suffix) ||
method.getName().equals("is" + suffix))) {
value = method.invoke(o, (Object[]) null);
....
}
}
。调用getSymbol()
还是getSymbol(Locale)
完全取决于Class.getMethods()
的返回顺序(没有任何特定的顺序)。我添加了一个测试,以确保方法有0个参数,我的问题解决了。
现在,一个奇怪的巧合,在我们第一次观察到这个行为的同一天,另一个人碰巧提交了一个相同的修复。
令人费解的是,这段代码已经运行了18个月没有任何问题,然后突然出现了这种行为。
当我创建一个测试用例时,它大约有50%的时间失败,正如人们所期望的那样(有2个匹配的方法,没有特定的顺序返回),所以我很困惑(惊讶)它工作了18个月,执行了10^5次。(可以想象,但不太可能,它已经失败了,但在随后的重试中成功了)。
纯粹出于好奇,我想知道Java运行时中是否有任何可能导致这种潜在行为突然出现的东西。更令人费解的是,这种行为的修复应该由其他人在一个成熟和稳定的项目的同一天提供-这可能表明相同的潜在行为突然在其他地方实现了自己。
那么,问题是:有人知道什么因素可能会影响Class.getMethods()
返回的方法的顺序吗?
从getMethods()
中可以看到的相关Java代码显示了对缓存数据的大量检查,但最终决定权取决于此实现:
private native Method[] getDeclaredMethods0(boolean publicOnly);
因此,它依赖于VM,并且仅仅因为"它多次是相同的"而信任顺序是一个非常糟糕的主意。
缓存肯定会影响它,因为如果它第一次被正确获取,它将在随后的时间也工作,除非缓存被清除(有一些软引用业务等正在进行)。