我有一个由Maven构建的应用程序,它是一个混合的Groovy和Java项目。
使用GMaven插件(v1.3),我可以轻松地针对Java和Groovy类运行Groovy测试。在应用程序的构建期间,我的java类与扩展的存根文件链接,这些文件从GroovyObject声明方法。
但是,如果我使用Java编写针对应用程序Groovy代码的测试并尝试从GroovyObject调用方法,则会导致编译时失败。
这有什么解决方法吗? GMaven是否有任何配置参数可以实现这一目标?
感谢。
这是我的pom中的build.plugins:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<includes>
<include>target/generated-sources/groovy-stubs/main</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>generateTestStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<!-- providerSelection probably defaults to 1.7 now -->
<providerSelection>1.7</providerSelection>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
这是java测试类:
public class JavaGroovyTest extends TestCase {
@Test
public void testGroovyClasses(){
Model m = new Model(); //Model is an application class written in Groovy
assertNotNull(m);
assertEquals(4,m.getMetaClass().getProperties().size());
}
}
这是编译器输出:
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
/Users/mkohout/Documents/trunk/src/test/java/JavaGroovyTest.java:[17,24] cannot find symbol
symbol : method getMetaClass()
location: class com.q.Model
答案 0 :(得分:2)
将groovy对象声明为GroovyObject。例如:
import groovy.lang.GroovyObject
public class JavaGroovyTest extends TestCase {
@Test
public void testGroovyClasses(){
GroovyObject m = new Model(); //Model is an application class written in Groovy
assertNotNull(m);
assertEquals(4,m.getMetaClass().getProperties().size());
}
}
编辑:更长的解释
groovy编译器向类添加getMetaClass
方法,但它将其标记为synthetic
。这是作为“实现细节”生成的方法和字段的内部JVM标志,并且不应对代码可见。您可以使用javap验证:
$ javap -verbose Model | grep -A18 getMetaClass\(\)
public groovy.lang.MetaClass getMetaClass();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: getfield #42; //Field metaClass:Lgroovy/lang/MetaClass;
4: dup
5: ifnull 9
8: areturn
9: pop
10: aload_0
11: dup
12: invokevirtual #28; //Method $getStaticMetaClass:()Lgroovy/lang/MetaClass;
15: putfield #42; //Field metaClass:Lgroovy/lang/MetaClass;
18: aload_0
19: getfield #42; //Field metaClass:Lgroovy/lang/MetaClass;
22: areturn
23: nop
Synthetic: true
你可以通过将它转换为groovy.lang.GroovyObject
接口来解决这个问题,该接口声明了getMetaClass
方法(这次不合成)。
这可能不是一个很好的解决方案,但是说,首先在Java中使用groovy metaClass可能是不明智的。如果在测试中不可能只使用groovy,我会考虑使用普通的java可访问方法从groovy类中公开所需的metaClass信息。
答案 1 :(得分:0)
简短回答:您无法从Java访问Groovy“元方法”。
答案很长:
DefaultGroovyMethods.getMetaClass(..)
不是可以静态编译为Java字节码的方法。
<强> [校正:强>
与此同时,Don发布了一个答案,建议转向GroovyObject
。这是正确的,这样你应该能够像这样调用Groovy元方法:
List<Book> books = (List<Book>)
((GroovyObject) Book).invokeMethod(
"findAllByAuthorAndTitle", new String[] {"author", "title"})
(或类似)。 - 然而,这对于日常编程来说是不切实际的。的 强>
查看DefaultGroovyMethods
ApiDocs。每个方法的第一个参数是对象的运行时类型。其余的相应方法形成了在Groovy代码中使用的方法签名。你已经熟悉了很多这些。
所有这些“运行时元方法”都不是静态编译到GroovyObject
((几乎)任何Groovy对象派生自该类型),但是,当被调用时,在运行时动态调度 - 通常使用GroovyObject.invokeMethod(String, Object)
method。
因此,您的Java代码在编译时调用一个根本不存在的方法。 - 但是,如果不编译为Java字节码,那么Groovy代码如何引用该方法? - Groovy(Java)字节码不直接引用方法(构造函数,属性等),而是构建在运行时调用以进行动态分派的结构。
例如,用Groovy编写的测试类将编译为此(为清晰起见缩短了):
public class JavaGroovyTest extends TestCase
implements GroovyObject
{
public JavaGroovyTest()
{
JavaGroovyTest this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass tmp12_9 = $getStaticMetaClass(); this.metaClass = ((MetaClass)ScriptBytecodeAdapter.castToType(tmp12_9, $get$$class$groovy$lang$MetaClass())); tmp12_9;
}
public void testGroovyClasses() { CallSite[] arrayOfCallSite = $getCallSiteArray(); Model m = arrayOfCallSite[0].callConstructor($get$$class$Model());
arrayOfCallSite[1].callStatic($get$$class$JavaGroovyTest(), m);
arrayOfCallSite[2].callStatic($get$$class$JavaGroovyTest(), $const$0, arrayOfCallSite[3].call(arrayOfCallSite[4].call(arrayOfCallSite[5].call(m)))); return;
}
}
当执行此代码时,Groovy运行时将执行数百,数千甚至数十亿甚至数十亿的查找;-)以便最终不直接调用方法,而是通过类似反射的invokeMethod(..)
调用
Groovy编程语言的大多数部分(包括构建器和其他库)都非常依赖meta programming这一概念,可以在compile-time或runtime实现。{{3}}。
不幸的是,Groovy更喜欢后者,虽然所有动态添加的功能都不编译为Java字节码,并且不可以直接由Java代码访问。