Используя 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 вызывается, это просто возвращается "очень отличный". Таким образом, сервис дразнят.

Однако я не понимаю почему второй метод, testMetaClass не работает как ожидалось, но вместо этого бросает NullPointerException при попытке вызвать callX на объекте службы. Я думал, что перезаписал doCallService метод с этой строкой:

pojo.metaClass.doCallService = { String s ->

Что я делаю неправильно?

Спасибо!

14
задан Esko 28 December 2009 в 21:02
поделиться

3 ответа

Ваш синтаксис немного неверен. Проблема в том, что pojo является Java-объектом и не имеет метакласса. Чтобы перехватить вызовы doCallService PlainOldJavaObject с помощью ExpandoMetaClass:

Просто замените:

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

на:

    PlainOldJavaObject.metaClass.doCallService = { String s ->
        "no service"
    }
18
ответ дан 1 December 2019 в 07:19
поделиться

То, что у вас есть, выглядит нормально. Я запустил немного измененную версию на отличном консольном веб-приложении, и она работала без проблем. Убедитесь сами, используя этот код на 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-язык развивается довольно быстро, и реализация метакласса меняется от версии к версии.

Edit - Feedback from Comment:

Версия groovy console webapp - 1.7-rc-1. Так что похоже, что эта версия может работать так, как вы хотите. В настоящее время они находятся в RC2, поэтому я ожидаю, что он скоро будет выпущен. Не уверен, что то, что вы видите, является ошибкой или просто разницей в том, как это работает в версии 1.6.x.

1
ответ дан 1 December 2019 в 07:19
поделиться

Если ваш 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.

.
18
ответ дан 1 December 2019 в 07:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: