Использование 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 ->
Что я делаю неправильно?
Спасибо!
3 ответа
Ваш синтаксис немного не в порядке. Проблема в том, что pojo является Java-объектом и не имеет метакласса. Чтобы перехватить вызовы doCallService PlainOldJavaObject с использованием ExpandoMetaClass:
Просто замените:
pojo.metaClass.doCallService = { String s ->
"no service"
}
С:
PlainOldJavaObject.metaClass.doCallService = { String s ->
"no service"
}
Если ваш 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.
То, что у вас есть, выглядит отлично. Я запустил слегка измененную версию в отличном веб-приложении консоли, и он работал без проблем. Убедитесь сами, используя этот код на 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 язык движется довольно быстро, и реализация метакласса меняется от версии к версии.
Редактировать - Отзыв от комментария:
Версия web-приложения с отличной консолью -1.7-rc-1. Похоже, что эта версия может работать так, как вы хотите. В настоящее время они находятся в RC2, так что я ожидаю, что он скоро выйдет. Не уверен, что то, что вы видите, это ошибка или просто разница в том, как она работает в версии 1.6.x.