使用 Groovy MetaClass 覆盖方法

发布于 2024-08-15 03:06:05 字数 1378 浏览 4 评论 0原文

我有一个使用服务来执行某些操作的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

感悟人生的甜 2024-08-22 03:06:05

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

只需将: 替换

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

为:

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

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:

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

With:

    PlainOldJavaObject.metaClass.doCallService = { String s ->
        "no service"
    }
尸血腥色 2024-08-22 03:06:05

如果您的 POJO 确实是 Java 类,而不是 Groovy 类,那么这就是您的问题。 Java 类不通过元类调用方法。例如,在 Groovy 中:

pojo.publicMethod('arg')

相当于 Java:

pojo.getMetaClass().invokeMethod('publicMethod','arg');

invokeMethod 通过元类发送调用。但是这个方法:

public String publicMethod(String x) {
    return doCallService(x);
}

是一个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:

pojo.publicMethod('arg')

is equivalent to this Java:

pojo.getMetaClass().invokeMethod('publicMethod','arg');

invokeMethod sends the call through the metaClass. But this method:

public String publicMethod(String x) {
    return doCallService(x);
}

is a Java method. It doesn't use invokeMethod to call doCallService. 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.

冷…雨湿花 2024-08-22 03:06:05

你所拥有的看起来不错。我在 groovy 控制台 web 应用程序上运行了一个稍微修改过的版本,它运行没有问题。请自行查看 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 版本中工作方式的差异。

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/.

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")

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文