Использование 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);
}
}
и у меня есть классный тестовый пример:
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
должен быть классный класс, чтобы все вызовы проходили метакласс. Обычный Java-код не использует метаклассы.
короче говоря: даже Groovy не может переопределить вызовы метода Java, он может только переопределять вызовы из Groovy или в противном случае отправлять через invokeMethod.
то, что у вас есть, выглядит нормально. Я запустил слегка измененную версию на нем на 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 в реализации metaclass. Язык groovy движется довольно быстро, и реализация метакласса изменяется от версии к версии.
Edit-обратная связь от Комментарий:
версия groovy консоли webapp 1.7-rc-1. Похоже, что версия может работать как вы хотите. В настоящее время они находятся в RC2, поэтому я ожидаю, что он будет выпущен в ближайшее время. Не уверен, что то, что вы видите, является ошибкой или просто разницей в том, как она работает в 1.6.X версии.