GroovyScriptEngine在加载使用其他类的类时引发MultipleCompationerRorsException';静态内部类
我在GroovyScriptEngine中遇到了一个问题—它似乎无法处理内部类。有人知道GroovyScriptEngine中是否存在一些限制或解决方法吗 我有一个包含以下两个文件的目录:GroovyScriptEngine在加载使用其他类的类时引发MultipleCompationerRorsException';静态内部类,groovy,Groovy,我在GroovyScriptEngine中遇到了一个问题—它似乎无法处理内部类。有人知道GroovyScriptEngine中是否存在一些限制或解决方法吗 我有一个包含以下两个文件的目录: //MyClass.groovy 公共类MyClass{ 肌外膜m1; MyOuter.MyInner m2; } 及 //MyOuter.groovy 公共级MyOuter{ 公共静态类MyInner{} } 我有以下的测试课程: import java.io.File; import java.net
//MyClass.groovy
公共类MyClass{
肌外膜m1;
MyOuter.MyInner m2;
}
及
//MyOuter.groovy
公共级MyOuter{
公共静态类MyInner{}
}
我有以下的测试课程:
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import groovy.util.GroovyScriptEngine;
public class TestGroovyScriptEngine {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
final File myGroovySourceDir = new File("C:/MyGroovySourceDir");
final URL[] urls = { myGroovySourceDir.toURL() };
GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine(urls,
Thread.currentThread().getContextClassLoader());
Class<?> clazz = groovyScriptEngine.getGroovyClassLoader().loadClass("MyClass");
}
}
我本来希望有一个“干净的编译”,但内部类似乎造成了问题
我的groovy类在使用groovyc的命令行或Eclipse中可以很好地编译。您在这里遇到了一个边缘案例。为了澄清发生了什么,让我们定义初始条件:
- 您有一个在JVM中执行的Java(或Groovy)类
- 有两个Groovy类在JVM之外加载
GroovyClassLoader
(扩展URLClassLoader
btw)在运行的JVM之外加载这两个Groovy类。我将尝试用最简单的话解释添加类型为MyOuter
的字段不会引发任何编译错误,但是MyOuter.MyInner
会引发任何编译错误
执行时:
Class<?> clazz = groovyScriptEngine.getGroovyClassLoader().loadClass("MyClass");
资料来源:
这里URL-source=resourceLoader.loadGroovySource(名称)代码>它将完整的文件URL加载到源文件,这里cls=recompile(源、名称、旧类)代码>它执行类编译
Groovy类编译涉及几个方面。其中之一是Phase.SEMANTIC\u ANALYSIS
,它分析类字段及其类型。此时,ClassCodeVisitorSupport
用于MyClass
类和以下行
node.visitContents(this);
开始类内容处理。如果我们看一下此方法的源代码:
public void visitContents(GroovyClassVisitor visitor) {
// now let's visit the contents of the class
for (PropertyNode pn : getProperties()) {
visitor.visitProperty(pn);
}
for (FieldNode fn : getFields()) {
visitor.visitField(fn);
}
for (ConstructorNode cn : getDeclaredConstructors()) {
visitor.visitConstructor(cn);
}
for (MethodNode mn : getMethods()) {
visitor.visitMethod(mn);
}
}
资料来源:
我们将看到它分析和处理类属性、字段、构造函数和方法。在此阶段,它解析为这些元素定义的所有类型。它看到有两个属性m1
和m2
,类型分别为MyOuter
和MyOuter.MyInner
,并执行visitor.visitProperty(pn)为他们编码>。此方法执行我们正在查找的方法-resolve()
资料来源:
此方法对MyOuter
和MyOuter.MyInner
类都执行。值得一提的是,类解析机制只检查给定类在类路径中是否可用,而不加载或解析任何类。这就是为什么当此方法到达resolvetooter(type)
时,MyOuter
会被识别。如果我们快速查看一下它的源代码,我们就会理解它为什么适用于这个类:
private boolean resolveToOuter(ClassNode type) {
String name = type.getName();
// We do not need to check instances of LowerCaseClass
// to be a Class, because unless there was an import for
// for this we do not lookup these cases. This was a decision
// made on the mailing list. To ensure we will not visit this
// method again we set a NO_CLASS for this name
if (type instanceof LowerCaseClass) {
classNodeResolver.cacheClass(name, ClassNodeResolver.NO_CLASS);
return false;
}
if (currentClass.getModule().hasPackageName() && name.indexOf('.') == -1) return false;
LookupResult lr = null;
lr = classNodeResolver.resolveName(name, compilationUnit);
if (lr!=null) {
if (lr.isSourceUnit()) {
SourceUnit su = lr.getSourceUnit();
currentClass.getCompileUnit().addClassNodeToCompile(type, su);
} else {
type.setRedirect(lr.getClassNode());
}
return true;
}
return false;
}
资料来源:
当Groovy类加载器尝试解析它到达的MyOuter
类型名时
lr=classNodeResolver.resolveName(名称,编译单元);
它定位名为MyOuter.groovy
的脚本,并创建与此脚本文件名关联的SourceUnit
对象。这很简单,就像说“好的,这个类目前不在我的类路径中,但是我可以看到一个源文件,一旦编译,它将提供一个有效类型的名称MyOuter
”。这就是为什么它最终达到:
currentClass.getCompileUnit().addClassNodeToCompile(类型,su);
其中currentClass
是一个与MyClass
类型关联的对象-它将此源单元添加到MyClass
编译单元,因此它使用MyClass
类进行编译。这就是解决问题的关键
MyOuter m1
类属性结束
在下一步中,它选择MyOuter.MyInner m2
属性并尝试解析其类型。请记住-MyOuter
已正确解析,但它没有加载到类路径,因此它的静态内部类在任何作用域中都不存在。它使用与MyOuter
相同的解析策略,但其中任何一种都适用于MyOuter.MyInner
类。这就是为什么最终抛出这个编译异常
变通办法
好的,我们知道会发生什么,但是我们能做些什么吗?幸运的是,这个问题有一个解决方法。只有先将MyOuter
类加载到Groovy脚本引擎,才能运行程序并成功加载MyClass
:
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import groovy.util.GroovyScriptEngine;
public class TestGroovyScriptEngine {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
final File myGroovySourceDir = new File("C:/MyGroovySourceDir");
final URL[] urls = { myGroovySourceDir.toURL() };
GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine(urls,
Thread.currentThread().getContextClassLoader());
groovyScriptEngine.getGroovyClassLoader().loadClass("MyOuter");
Class<?> clazz = groovyScriptEngine.getGroovyClassLoader().loadClass("MyClass");
}
}
导入java.io.File;
导入java.net.MalformedURLException;
导入java.net.URL;
导入groovy.util.groovyScript引擎;
公共类TestGroovyScript引擎{
publicstaticvoidmain(字符串[]args)引发畸形的DurLexException,ClassNotFoundException{
最终文件myGroovySourceDir=新文件(“C:/myGroovySourceDir”);
最终URL[]URL={myGroovySourceDir.toURL()};
GroovyScript引擎GroovyScript引擎=新的GroovyScript引擎(URL,
Thread.currentThread().getContextClassLoader());
groovyScriptEngine.getGroovyClassLoader().loadClass(“MyOuter”);
clazz类=groovyScriptEngine.getGroovyClassLoader().loadClass(“MyClass”);
}
}
它为什么有效?嗯,MyOuter
类的语义分析不会引起任何问题,因为在这个阶段所有类型都是已知的。这就是加载MyOuter
类成功的原因,它导致Groovy脚本引擎实例知道MyOuter
和M是什么
private boolean resolveToOuter(ClassNode type) {
String name = type.getName();
// We do not need to check instances of LowerCaseClass
// to be a Class, because unless there was an import for
// for this we do not lookup these cases. This was a decision
// made on the mailing list. To ensure we will not visit this
// method again we set a NO_CLASS for this name
if (type instanceof LowerCaseClass) {
classNodeResolver.cacheClass(name, ClassNodeResolver.NO_CLASS);
return false;
}
if (currentClass.getModule().hasPackageName() && name.indexOf('.') == -1) return false;
LookupResult lr = null;
lr = classNodeResolver.resolveName(name, compilationUnit);
if (lr!=null) {
if (lr.isSourceUnit()) {
SourceUnit su = lr.getSourceUnit();
currentClass.getCompileUnit().addClassNodeToCompile(type, su);
} else {
type.setRedirect(lr.getClassNode());
}
return true;
}
return false;
}
MyOuter m1
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import groovy.util.GroovyScriptEngine;
public class TestGroovyScriptEngine {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
final File myGroovySourceDir = new File("C:/MyGroovySourceDir");
final URL[] urls = { myGroovySourceDir.toURL() };
GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine(urls,
Thread.currentThread().getContextClassLoader());
groovyScriptEngine.getGroovyClassLoader().loadClass("MyOuter");
Class<?> clazz = groovyScriptEngine.getGroovyClassLoader().loadClass("MyClass");
}
}