使用 Groovy MetaClass 覆盖方法
我有一个使用服务来执行某些操作的 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
按预期工作: POJO 是使用 IService 的动态实现创建的
。当调用 callX 时,它只是返回“very groovy”。这样,服务就被模拟出来了。
但是我不明白为什么第二种方法 testMetaClass
不能按预期工作,而是在尝试在服务对象上调用 callX
时抛出 NullPointerException。我以为我已经用这一行覆盖了 doCallService
方法:
pojo.metaClass.doCallService = { String s ->
我做错了什么?
谢谢!
I have a POJO that uses a service to do something:
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);
}
}
And I have a Groovy test case:
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")
}
}
The first test method, testInjectedMockIFace
works as expected: The POJO is created with a dynamic implementation of IService
. When callX
is invoked, it simply returns "very groovy". This way, the service is mocked out.
However I don't understand why the second method, testMetaClass
does not work as expected but instead throws a NullPointerException when trying to invoke callX
on the service object. I thought I had overwritten the doCallService
method with this line:
pojo.metaClass.doCallService = { String s ->
What am I doing wrong?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你的语法有点不对劲。问题是pojo是一个Java对象并且没有metaClass。要使用 ExpandoMetaClass 拦截对 PlainOldJavaObject 的 doCallService 的调用:
只需将: 替换
为:
Your syntax is a tiny bit off. The problem is that pojo is a Java Object and does not have a metaClass. To intercept calls to PlainOldJavaObject's doCallService using ExpandoMetaClass:
Just replace:
With:
如果您的 POJO 确实是 Java 类,而不是 Groovy 类,那么这就是您的问题。 Java 类不通过元类调用方法。例如,在 Groovy 中:
相当于 Java:
invokeMethod
通过元类发送调用。但是这个方法:是一个Java方法。它不使用
invokeMethod
来调用doCallService
。为了让您的代码正常工作,PlainOldJavaObject
需要是一个 Groovy 类,以便所有调用都将通过元类。普通的 Java 代码不使用元类。简而言之:即使 Groovy 也无法覆盖 Java 方法调用,它只能覆盖来自 Groovy 的调用或通过 invokeMethod。
If your POJO really is a Java class, and not a Groovy class, then that is your problem. Java classes don't invoke methods via the metaClass. e.g., in Groovy:
is equivalent to this Java:
invokeMethod
sends the call through the metaClass. But this method:is a Java method. It doesn't use
invokeMethod
to calldoCallService
. To get your code to work,PlainOldJavaObject
needs to be a Groovy class so that all calls will go through the metaClass. Normal Java code doesn't use metaClasses.In short: even Groovy can't override Java method calls, it can only override calls from Groovy or that otherwise dispatch through invokeMethod.
你所拥有的看起来不错。我在 groovy 控制台 web 应用程序上运行了一个稍微修改过的版本,它运行没有问题。请自行查看 http://groovyconsole.appspot.com/ 中使用此代码。
您使用的是哪个版本的 Groovy。这很可能是 Groovy 元类实现中的一个错误。 Groovy 语言发展得非常快,元类实现也随着版本的不同而变化。
编辑 - 评论反馈:
groovy 控制台 web 应用程序的版本是 1.7-rc-1。所以看起来这个版本可以按照你想要的方式工作。它们目前处于 RC2 阶段,所以我预计它很快就会发布。不确定您所看到的是错误还是只是 1.6.x 版本中工作方式的差异。
What you have looks fine. I ran a slightly modified version on it on the groovy console webapp and it ran without issue. See for yourself using this code at http://groovyconsole.appspot.com/.
What version of Groovy are you using. It could very well be a bug in Groovy in the metaclass implementation. The groovy language moves pretty quickly and the metaclass implementation changes from version to version.
Edit - Feedback from Comment:
The version of the groovy console webapp is 1.7-rc-1. So it looks like that version may work as you want it to. They are currently in RC2 so I expect it would be released soon. Not sure if what you are seeing is a bug or just a difference in how it works in the 1.6.x version.