我们有一大堆像这样的代码
// semi-pseudo code
def result = someList.find { condition == true }
(someList
可能为null,但在groovy中可以正常,因为null.find{…}
工作正常。)
这行代码在grails控制器的操作中运行,并在生产中部署到服务器。经过一段时间(有时几小时,有时更长),上面的代码行将开始抛出一个NullPointerException - 一旦它开始抛出NPE,它总是抛出NPE。
通过调试,我们已经证明即使someList为null也一直正常工作(直到我们得到看似随机的第一个NPE)...也通过调试我们能够得到更详细的堆栈跟踪,表明Groovy的MetaClassRegistryImpl中存在错误.java line 214.
我已经用谷歌搜索了我能想到的每一个组合,看看是否有任何已知的Groovy错误但没有发现任何有价值的东西。
(使用Grails 1.3.7,因此使用Groovy 1.7.8)
JMeter脚本设置为运行一系列站点交互,使此问题半可重复。该脚本将迭代50-100系列,然后错误开始出现 - 一旦出现错误,它总是出错,直到将应用程序重新部署到服务器(Glassfish)。
跟踪groovy代码看起来像这样:
//AbstractCallSite.java
public Object call(Object receiver, Object arg1) throws Throwable {
return call(receiver, ArrayUtil.createArray(arg1));
}
//PerInstancePojoMetaClassSite.java
public Object call(Object receiver, Object[] args) throws Throwable {
if (info.hasPerInstanceMetaClasses()) {
try {
return InvokerHelper.getMetaClass(receiver).invokeMethod(receiver, name, args);
} catch (GroovyRuntimeException gre) {
throw ScriptBytecodeAdapter.unwrap(gre);
}
} else {
return CallSiteArray.defaultCall(this, receiver, args);
}
}
//InvokerHelper.java
public static MetaClass getMetaClass(Object object) {
if (object instanceof GroovyObject)
return ((GroovyObject) object).getMetaClass();
else
return ((MetaClassRegistryImpl) GroovySystem.getMetaClassRegistry()).getMetaClass(object);
}
//MetaClassRegistryImpl.java
public MetaClass getMetaClass(Object obj) {
return ClassInfo.getClassInfo(obj.getClass()).getMetaClass(obj);
}
所以NPE似乎在obj.getClass()
- 如果是这样的话,我有点困惑当someList
为空时它是如何工作的(但这是一个单独的主题)。
FWIW,我们 在someList
上进行我们自己的任何类或实例级元类编码。
Groovy中是否存在错误或者我们可能做错什么导致Groovy代码深入(随机)NPE?
UPDATE -
观察结果是someList
被设置为'java null'而不是'groovy null'(NullObject
)。对象来自地图(流程上下文),通过控制器动作中的流程......
class SomeController {
def someActionFlow = {
action {
def someList = flow.someList
}
}
}
有问题的情况是,从未设置flow.someList时,它应始终为null(groovy null)。 flow
只是一张地图,因此与执行flow.get('someList')
上面的代码适用于未知数量的迭代,然后开始返回'java nulls'而不是'groovy nulls'。
答案 0 :(得分:3)
我会冒险猜测它取决于someList
的创建方式。也就是说,如果它是在Groovy中创建的
def someList = null
然后Groovy将NullObject
分配给变量。但是,如果该值从其他Java组件返回为真正的Java null
,那么它将抛出NPE。更进一步,Groovy / Java / JVM中可能存在一些优化,其中callsite缓存导致它始终返回NPE。
然后,这只是一个疯狂的猜测。
答案 1 :(得分:1)
修正:与GROOVY-5248(调用网站缓存缺少空检查)类似,添加接收方空检查以避免在某些情况下使用NPE
提交641c6a8d4b6b3046f4d8a1a2ac5f08f1f2769f0f