4

ClassNotFound (はい、知っています。この例外に関する投稿がたくさんあります。ここや他の場所で検索しましたが、説明が見つかりませんでした)

Class.forName が失敗するのはなぜですか?

groovy> class Foo { 
groovy> } 
groovy> def f = new Foo() 
groovy> def cname = f.getClass().getName() 
groovy> def p = f.getClass().getPackage() 
groovy> def l = f.getClass().getClassLoader() 
groovy> println "Foo class name: $cname" 
groovy> println "Foo package: $p" 
groovy> println "Foo class loader: ${f.getClass().getClassLoader().toString()}" 
groovy> println "Current class loader: ${this.getClass().getClassLoader().toString()}" 
groovy> try { 
groovy>     Class.forName(cname) 
groovy> } catch (Exception e) { 
groovy>     println e 
groovy> } 
groovy> l.findClass("Foo") 



Foo class name: Foo
Foo package: null
Foo class loader: groovy.lang.GroovyClassLoader$InnerLoader@2d275595
Current class loader: groovy.lang.GroovyClassLoader$InnerLoader@2d275595
java.lang.ClassNotFoundException: Foo
Exception thrown
Oct 16, 2012 4:43:28 PM org.codehaus.groovy.runtime.StackTraceUtils sanitize
WARNING: Sanitizing stacktrace:
java.lang.ClassNotFoundException: Foo

ありがとう!

4

2 に答える 2

3

与えられた答えは基本的に正しいですが、重要な情報が 1 つ欠けています。Class.forName(String)Javaから呼び出されるJava用のJavaメソッドです。指定されたクラスをロードするには、クラスローダーを取得する必要があります。そして、内部メソッドを使用して呼び出しスタックをたどってローダーを取得します。Java では 1 レベル上がることは通常正しいことですが、Groovy では正しくありません。Groovy の各メソッド呼び出しには、生成されたメソッド、invokedynamic、リフレクションからの可変数の中間呼び出しスタック要素を含めることができます。ただし、通常、親呼び出しスタック フレームには、実際の呼び出し元クラスが含まれていません。代わりに、groovy ランタイムのローダー、さらには Java ランタイムのローダーになります。シェルのクラスはそれらの子にあるため、ローダーが要求されたクラスを見つけることができません。

于 2013-08-23T08:10:34.573 に答える
2

これは ClassLoader によるものです。シェル内の ClassLoader (つまり、シェル内で定義するクラス) は、シェルを実行する ClassLoader (シェルを実行するために必要な jar) とは異なります。Class.forName("Foo", true, this.class.classLoader)そのため、シェル内で ClassLoader を指定するため、コマンドが機能します

試す

def shell=new GroovyShell()

def f=shell.evaluate("class Foo{Foo(){println this.class.classLoader}};def f=new Foo()")

println shell.class.classLoader
shell.evaluate("println this.class.classLoader")

println "-----------"
println Class.forName("Foo", true, f.class.classLoader)
println Class.forName("Foo", true, this.class.classLoader)

2 番目ではなく、最初の Class.forName が機能することがわかります。スクリプトの実行は、シェルの ClassLoader を共有しないスクリプト クラスを作成するため、同様です。

Class.forName を実行しても、スクリプトのコンテキストで this と同じ this は使用されません。

それが十分に明確であるかどうかわかりません:(

于 2012-10-18T07:12:50.210 に答える