使用Groovy元类覆盖方法
我有一个使用服务做的东西POJO:使用Groovy元类覆盖方法
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
作品如预期的那样:POJO创建的动态实现为IService
。当调用callX
时,它仅返回“非常常见”。这样,服务就被嘲笑了。
但是我不明白为什么第二种方法testMetaClass
不能按预期方式工作,而是在尝试调用服务对象上的callX
时抛出NullPointerException。我想我已经覆盖了doCallService
方法这一行:
pojo.metaClass.doCallService = { String s ->
我在做什么错?
谢谢!
您的语法稍微偏离。问题是pojo是一个Java对象,并没有metaClass。为了拦截PlainOldJavaObject的doCallService使用ExpandoMetaClass电话:
只需更换:
pojo.metaClass.doCallService = { String s ->
"no service"
}
有了:
PlainOldJavaObject.metaClass.doCallService = { String s ->
"no service"
}
你看起来不错。我在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的控制台web应用程序的版本是1.7 RC-1。所以它看起来像这个版本可能会按照你的想法工作。他们目前在RC2中,所以我预计它很快就会发布。不确定你看到的是一个错误还是仅仅是它在1.6.x版本中的工作方式上的差异。
如果你的POJO真的是Java类,而不是Groovy类,那么这是你的问题。 Java类不通过metaClass调用方法。例如,在Groovy:
pojo.publicMethod('arg')
相当于该Java:
invokeMethod
发送通过元类呼叫。但是这种方法:
public String publicMethod(String x) {
return doCallService(x);
}
是一种Java方法。它不使用invokeMethod
拨打doCallService
。为了让你的代码正常工作,PlainOldJavaObject
需要是一个Groovy类,这样所有的调用都会通过metaClass。普通的Java代码不使用元类。
简而言之:即使Groovy不能覆盖Java方法调用,它只能覆盖来自Groovy的调用或者通过invokeMethod调度。
这里要记住的一件事是,当你操作类的metaClass,从这个点向前的每个实例将被操纵。这可能会对同一会话中运行的其他测试产生重大影响。当您操作某个类的实例时,只有该实例受到影响。 – 2013-05-06 15:58:17