12

tengo un POJO que utiliza un servicio de hacer algo:Usando maravilloso MetaClass sobrescribir métodos

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); 
    } 
} 

Y tengo un caso de prueba maravillosa:

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") 
    } 
} 

El primer método de ensayo, testInjectedMockIFace obras como se esperaba: POJO se crea con una implementación dinámica de IService. Cuando se invoca callX, simplemente devuelve "muy groovy". De esta manera, el servicio es burlado.

Sin embargo, no entiendo por qué el segundo método, testMetaClass no funciona como se esperaba, sino que arroja una NullPointerException al intentar invocar callX en el objeto de servicio. Pensé que había sobrescrito el método doCallService con esta línea:

pojo.metaClass.doCallService = { String s -> 

¿Qué estoy haciendo mal?

Gracias!

Respuesta

16

Su sintaxis es un poco escasa. El problema es que pojo es un objeto de Java y no tiene un metaClass.Para interceptar llamadas a doCallService utilizando ExpandoMetaClass de PlainOldJavaObject:

basta con sustituir:

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

Con:

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

Una cosa a tener en cuenta aquí es cuando manipulas la clase metaClass, cada instancia desde ese punto en adelante será manipulada. Esto puede tener un gran impacto en otras pruebas que se ejecutan en la misma sesión. Cuando manipulas una instancia de una clase, solo esa instancia se ve afectada. –

1

Lo que tiene se ve bien. Ejecuté una versión ligeramente modificada en la aplicación de la consola Groovy y se ejecutó sin problemas. Compruébalo usando este código en 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") 

¿Qué versión de Groovy utiliza? Podría ser un error en Groovy en la implementación de la metaclase. El lenguaje groovy se mueve bastante rápido y la implementación de la metaclase cambia de una versión a otra.

Editar - Comentarios del comentario:

La versión de la aplicación web de la consola maravilloso es 1,7-RC-1. Así que parece que esa versión puede funcionar como lo desees. Actualmente están en RC2, así que espero que se publique pronto. No estoy seguro de si lo que está viendo es un error o simplemente una diferencia en cómo funciona en la versión 1.6.x.

+0

Hola Chris, corro maravilloso Versión: 1.6.5 JVM: 1.6.0_13 – raoulsson

+0

La versión no tiene nada que ver con eso. El problema es que doCallService (x) es código Java, no código Groovy, por lo que no es consciente de metaClass. – noah

15

Si su POJO es realmente una clase de Java, y no una clase de Groovy, entonces ese es su problema. Las clases de Java no invocan métodos a través de metaClass. por ejemplo, en Groovy:

pojo.publicMethod('arg') 

es equivalente a este Java:

invokeMethod envía la llamada a través de la metaclase. Pero este método:

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

es un método de Java. No utiliza invokeMethod para llamar al doCallService. Para que su código funcione, PlainOldJavaObject debe ser una clase Groovy para que todas las llamadas pasen a través de la metaClass. El código Java normal no usa metaClasses.

En resumen: incluso Groovy no puede anular las llamadas al método Java, solo puede anular las llamadas de Groovy o que de otra manera se envían por invokeMethod.

+0

¿Cómo se puede distinguir correctamente entre el código Groovy y el código Java? ¿Cómo haría PlainOldGroovyObject en lugar de PlainOldJavaObject? – Tomato

+4

Si está en un archivo .groovy, es una clase Groovy. – noah

+0

lo tengo. gracias por la aclaración :) – Tomato