12

У меня есть POJO, который использует услугу, чтобы сделать что-то:Использование Groovy метакласса перезаписать методы

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

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

Спасибо!

ответ

16

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

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

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

С:

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

Одна вещь, о которой нужно помнить, это когда вы манипулируете метаклассом класса, каждый экземпляр с этой точки вперёд будет управляться. Это может оказать большое влияние на другие тесты, которые выполняются в одном сеансе. Когда вы манипулируете экземпляром класса, затрагивается только этот экземпляр. –

1

Что у вас хорошо выглядит. Я провел немного измененную версию на ней на groovy console 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 в реализации метакласса. Ровный язык перемещается довольно быстро, и реализация метакласса изменяется от версии к версии.

Edit - Отклики Комментарий:

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

+0

Привет Крис, я бегу Groovy Версия: 1.6.5 JVM: 1.6.0_13 – raoulsson

+0

версия не имеет к этому никакого отношения. Проблема в том, что doCallService (x) - это код Java, а не код Groovy, поэтому он не является metaClass. – noah

15

Если ваш POJO действительно является классом Java, а не классом Groovy, то это ваша проблема. Классы Java не вызывают методы через metaClass. например, в Groovy:

pojo.publicMethod('arg') 

эквивалентна этой Java:

invokeMethod посылает вызов через метакласса. Но этот метод:

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

- метод Java. Он не использует invokeMethod для звонка doCallService. Чтобы заставить ваш код работать, PlainOldJavaObject должен быть классом Groovy, чтобы все вызовы проходили через metaClass. Обычный Java-код не использует metaClasses.

Вкратце: даже Groovy не может переопределять вызовы методов Java, он может только отменять вызовы Groovy или отправлять их через invokeMethod.

+0

Как вы правильно различаете код Groovy и код Java? Как вы могли бы сделать PlainOldGroovyObject вместо PlainOldJavaObject? – Tomato

+4

Если он находится в файле .groovy, это класс Groovy. – noah

+0

получил. спасибо за разъяснение :) – Tomato