16

我有一个使用服务做某事的 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被调用时,它只是简单地返回“非常时髦”。这样,服务就被模拟出来了。

但是我不明白为什么第二种方法testMetaClass不能按预期工作,而是在尝试调用callX服务对象时抛出 NullPointerException。我以为我已经doCallService用这一行覆盖了该方法:

pojo.metaClass.doCallService = { String s ->

我究竟做错了什么?

谢谢!

4

3 回答 3

20

你的语法有点不对劲。问题是 pojo 是一个 Java 对象并且没有元类。使用 ExpandoMetaClass 拦截对 PlainOldJavaObject 的 doCallService 的调用:

只需更换:

    pojo.metaClass.doCallService = { String s ->
        "no service"
    }

和:

    PlainOldJavaObject.metaClass.doCallService = { String s ->
        "no service"
    }
于 2010-03-08T20:12:52.113 回答
18

如果您的 POJO 确实是 Java 类,而不是 Groovy 类,那么这就是您的问题。Java 类不通过元类调用方法。例如,在 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 代码不使用元类。

简而言之:即使 Groovy 也无法覆盖 Java 方法调用,它只能覆盖来自 Groovy 的调用或通过invokeMethod调度的调用。

于 2009-12-28T20:59:54.680 回答
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 版本中的工作方式不同。

于 2009-12-18T12:21:53.757 回答