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不能按預期方式工作,而是在嘗試調用服務對象上的callX時拋出NullPointerException。我想我已經覆蓋了doCallService方法這一行:

pojo.metaClass.doCallService = { String s -> 

我在做什麼錯?

謝謝!

回答

16

您的語法稍微偏離。問題是pojo是一個Java對象,並沒有metaClass。爲了攔截PlainOldJavaObject的doCallService使用ExpandoMetaClass電話:

只需更換:

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

有了:

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

這裏要記住的一件事是,當你操作類的metaClass,從這個點向前的每個實例將被操縱。這可能會對同一會話中運行的其他測試產生重大影響。當您操作某個類的實例時,只有該實例受到影響。 – 2013-05-06 15:58:17

1

你看起來不錯。我在groovy控制檯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中的一個錯誤。 groovy語言移動速度非常快,並且元類實現從版本變爲版本。

編輯 - 反饋的評論:

Groovy的控制檯web應用程序的版本是1.7 RC-1。所以它看起來像這個版本可能會按照你的想法工作。他們目前在RC2中,所以我預計它很快就會發布。不確定你看到的是一個錯誤還是僅僅是它在1.6.x版本中的工作方式上的差異。

+0

嗨克里斯,我運行的Groovy版本:1.6.5 JVM:1.6.0_13 – raoulsson 2009-12-18 12:45:00

+0

版本與它無關。問題是doCallService(x)是Java代碼,而不是Groovy代碼,所以它不是metaClass。 – noah 2009-12-28 21:01:03

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代碼不使用元類。

簡而言之:即使Groovy不能覆蓋Java方法調用,它只能覆蓋來自Groovy的調用或者通過invokeMethod調度。

+0

如何正確區分Groovy代碼和Java代碼?你將如何使PlainOldGroovyObject而不是PlainOldJavaObject? – Tomato 2012-12-13 10:36:44

+4

如果它位於.groovy文件中,則它是Groovy類。 – noah 2012-12-13 15:37:09

+0

明白了。感謝澄清:) – Tomato 2012-12-13 17:52:24