Использование Groovy MetaClass для перезаписи методов - PullRequest
14 голосов
/ 18 декабря 2009

У меня есть 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 ->

Что я делаю не так?

Спасибо!

Ответы [ 3 ]

18 голосов
/ 28 декабря 2009

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

18 голосов
/ 08 марта 2010

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

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

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

С:

    PlainOldJavaObject.metaClass.doCallService = { String s ->
        "no service"
    }
1 голос
/ 18 декабря 2009

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

Редактировать - Отзыв на комментарий:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...