我有一个使用服务做某事的POJO:
public class PlainOldJavaObject {
private IService service;
public String publicMethod(String x) {
return doCallService(x);
}
public String doCallService(String x) {
if(service == null) {
throw new RuntimeException("Service must not be null");
}
return service.callX(x);
}
public interface IService {
String callX(Object o);
}
}
我有一个Groovy测试用例:
class GTest extends GroovyTestCase {
def testInjectedMockIFace() {
def pojo = new PlainOldJavaObject( service: { callX: "very groovy" } as IService )
assert "very groovy" == pojo.publicMethod("arg")
}
def testMetaClass() {
def pojo = new PlainOldJavaObject()
pojo.metaClass.doCallService = { String s ->
"no service"
}
assert "no service" == pojo.publicMethod("arg")
}
}
第一个测试方法testInjectedMockIFace
按预期工作:使用动态实现IService
创建POJO。调用callX
时,它只返回“非常常规”。这样,服务就被嘲笑了。
但是我不明白为什么第二种方法testMetaClass
不能按预期工作,而是在尝试在服务对象上调用callX
时抛出NullPointerException。我以为我用这一行覆盖了doCallService
方法:
pojo.metaClass.doCallService = { String s ->
我做错了什么?
谢谢!
答案 0 :(得分:18)
如果您的POJO确实是Java类,而不是Groovy类,那么这就是您的问题。 Java类不通过metaClass调用方法。例如,在Groovy中:
pojo.publicMethod('arg')
等同于这个Java:
pojo.getMetaClass().invokeMethod('publicMethod','arg');
invokeMethod
通过metaClass发送调用。但是这个方法:
public String publicMethod(String x) {
return doCallService(x);
}
是一种Java方法。它不会使用invokeMethod
来调用doCallService
。为了使代码工作,PlainOldJavaObject
需要是一个Groovy类,以便所有调用都将通过metaClass。普通的Java代码不使用metaClasses。
简而言之:即使Groovy也无法覆盖Java方法调用,它只能覆盖来自Groovy的调用,或者通过invokeMethod调度。
答案 1 :(得分:18)
你的语法有点偏。问题是pojo是一个Java对象,没有metaClass。使用ExpandoMetaClass拦截对PlainOldJavaObject的doCallService的调用:
只需替换:
pojo.metaClass.doCallService = { String s ->
"no service"
}
使用:
PlainOldJavaObject.metaClass.doCallService = { String s ->
"no service"
}
答案 2 :(得分:1)
你看起来很好。我在groovy控制台webapp上运行了一个稍微修改过的版本,它运行没有问题。请在http://groovyconsole.appspot.com/使用此代码自行查看。
public interface IService {
String callX(Object o);
}
public class PlainOldJavaObject {
private IService service;
public String publicMethod(String x) {
return doCallService(x);
}
public String doCallService(String x) {
if(service == null) {
throw new RuntimeException("Service must not be null");
}
return service.callX(x);
}
}
def pojo = new PlainOldJavaObject()
pojo.metaClass.doCallService = { String s ->
"no service"
}
println pojo.publicMethod("arg")
您使用的是什么版本的Groovy。它很可能是元类实现中Groovy中的一个错误。 groovy语言移动得非常快,元类实现从版本变为版本。
编辑 - 评论的反馈:
groovy控制台webapp的版本是1.7-rc-1。因此看起来该版本可能会按照您的要求运行。他们目前在RC2,所以我希望它很快就会发布。不确定你看到的是一个错误,还是它在1.6.x版本中的工作方式不同。