找不到合适的类加载器可供抓取
我在课程的开头有这样的内容:
@Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2')
class MyClass{...
我正在尝试对此类进行单元测试,但是每当我尝试运行 JUnit 4 测试时,我都会收到此错误:
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:198)
at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:163)
at groovy.grape.GrapeIvy$chooseClassLoader.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:149)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:227)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSite.invoke(PogoMetaMethodSite.java:225)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:51)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:153)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:216)
at groovy.grape.Grape.grab(Grape.java:131)
at groovy.grape.Grape$grab.callStatic(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:173)
at ammoscanner.AmmoScanner.<clinit>(AmmoScanner.groovy)
... 30 more
有什么想法吗?我正在使用 groovy 1.7.5
I have this at the beginning of a class:
@Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2')
class MyClass{...
I'm trying to unit test this class, but whenever I try to run JUnit 4 tests, I get this error:
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:198)
at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:163)
at groovy.grape.GrapeIvy$chooseClassLoader.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:149)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:227)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSite.invoke(PogoMetaMethodSite.java:225)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:51)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:153)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:216)
at groovy.grape.Grape.grab(Grape.java:131)
at groovy.grape.Grape$grab.callStatic(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:173)
at ammoscanner.AmmoScanner.<clinit>(AmmoScanner.groovy)
... 30 more
Any ideas? I'm using groovy 1.7.5
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
问题
查看源代码,只要提供的 ClassLoader 的名称(或其超类)不是
groovy.lang.GroovyClassLoader
或org.codehaus.groovy.tools.RootLoader
,就会引发此异常代码>.即目标类加载器必须是上述类的实例(恕我直言,有点限制)。解决方案
目前我不知道如何使用
@Grape
/@Grab
/@GrabConfig
注解配置特定的类加载器。最接近的方法是使用@GrabConfig(systemClassLoader=true),并确保系统类加载器是上述 ClassLoader 类之一的实例。如果有人知道,请告诉我(我会更新这个答案)。
解决方法
以下代码将以编程方式下载您的 Grapes,并将它们加载到提供的 GroovyClassLoader (诚然,这不完全是你想要的)。
The Problem
Looking at the source code, this exception is thrown whenever the supplied ClassLoader's name (or it's superclasses) is not
groovy.lang.GroovyClassLoader
ororg.codehaus.groovy.tools.RootLoader
. i.e. The target classloader must be an instance of the aforementioned classes (a bit restrictive IMHO).A Solution
Currently I don't know how to configure a specific classloader using
@Grape
/@Grab
/@GrabConfig
annotations. The closest would be to use@GrabConfig(systemClassLoader=true)
, and ensure the System classloader is an instance of one of the above ClassLoader classes.If anyone does know, please let me know (and I'll update this answer).
A Workaround
The following code will programmatically download your Grapes, and load them into the supplied GroovyClassLoader (admittedly, not quite what you want).
使用
@Grab
会使代码无法测试,至少截至 2011 年 1 月 26 日是这样。Using
@Grab
makes code untestable, at least as of 01/26/2011.对我有用的解决方案(都用于在 IntelliJ 中使用
@Grab
和通过 Maven 运行脚本测试):pom 中引用通过
文件(无论如何,这对于更好的编码体验很有用):@Grab
使用的依赖项.xml例如,我的 Groovy 脚本中有以下
@Grab
:因此我添加以下 Maven 依赖项:
pom.xml
文件中的ivy
(需要在 IDE 中正确处理@Grab
):groovy.grape.enable
系统属性设置为false
。 这是解决方案的主要和关键部分 - 它禁用脚本的@Grab
注释处理,但请记住我们已经在 Mavenpom 中引用了这些依赖项.xml
文件:该解决方案的缺点是您必须在
@Grab
和 Mavenpom.xml
文件中复制依赖项,但同样,如果您开发您经常这样做的 Groovy 脚本是为了改善您的编码体验(获得更好的代码突出显示等)。Solution that worked for me (both for running tests for scripts using
@Grab
in IntelliJ and via Maven):@Grab
in your Mavenpom.xml
file (this is useful anyway for better coding experience):E.g. I have the following
@Grab
in my Groovy script:So I add the following Maven dependency:
ivy
in your Mavenpom.xml
file (needed to handle@Grab
properly in your IDE):groovy.grape.enable
system property tofalse
. This is the main and crucial part of the solution - it disables@Grab
annotation processing for the script, but remember we already have those dependencies referenced in our Mavenpom.xml
file:The downside of the solution is that you have to duplicate your dependencies in
@Grab
and Mavenpom.xml
file but again, if you develop a Groovy script you oftentimes already do so to improve your coding experience (get better code highlights etc).我假设您尝试过
像这样添加:
I assume you've tried adding
like so:
如果您没有使用 systemClassLoader=true ,那么您的 IDE 似乎没有使用 groovy 编译器运行代码,您可以使用一个简单的 groovy 类来检查,该类输出其类加载器的类名。我猜它会尝试编译 groovy 类并使用非 groovy 类加载器运行它们。
另请参阅此答案对转换期间的常规错误:找不到合适的类加载器供抓取。另外这篇博文解释了有关跑步的更多信息使用库存类加载器预编译 Groovy 类。
If you are not using systemClassLoader=true then it seems your IDE is not rrunning the code with a groovy compiler, you can check that with a simple groovy class that outputs the class name of its classloader. I would guess it tries to compile the groovy classes and run them with a non-groovy classloader.
See also this answer to General error during conversion: No suitable ClassLoader found for grab. Also this blog post explains more about running pre-compiled groovy classes with the stock classloader.
添加 Kepler 的插件快照更新站点。
这似乎解决了“..没有合适的类加载器问题”。不幸的是,在此之后我仍然必须将葡萄存储库添加到项目的类路径中。
Add the plugin snapshot update site for Kepler.
This seems to solve the "..no suitable classloader problem". Unfortunately, I still had to add the grape repo to the classpath for the project after this.
还有一种使用
@Grab
注释测试类的解决方案:@Grab
注释移至此类。然后让这个类成为一个简单的包装器,它只是将所有消息传递给原始类。@Grab
时,请使用包装器。There's one more solution for testing a class with
@Grab
annotation:@Grab
annotation to this class. Then make this class a simple wrapper, which just passes all the messages to the original class.@Grab
, use the wrapper.这个问题有一个解决办法!
您可以使用 Groovy 的元编程来重写负责确定类加载器是 groovy.lang.GroovyClassLoader 的实例还是 org.codehaus.groovy.tools.RootLoader 的实例的方法。
由于 这个 Groovy 错误,您无法使用元编程覆盖私有方法,否则您可以继续通过执行以下操作来更改
isValidTargetClassLoaderClass
方法:但是,
isValidTargetClassLoaderClass
是由isValidTargetClassLoader
(另一个私有方法)调用的,该方法称为通过chooseClassLoader
,这是一个 public 方法,可以使用元编程重写:我所做的就是替换对
!isValidTargetClassLoader 的任何调用
与loader?.class == null
。现在,我正在使用 Spock,因此我将此代码放入测试类的
setupSpec
方法中。但是,如果您使用 JUnit,我想它会想要进入用@BeforeClass
注释的方法。下面是它与 Spock 一起工作的示例(注意 IntelliJ 显示它将返回一个通常会抛出异常的类加载器:
There is a solution to this!
You can use Groovy's metaprogramming to override the methods responsible for determining if the class loader is an instance of
groovy.lang.GroovyClassLoader
ororg.codehaus.groovy.tools.RootLoader
.Because of this Groovy bug, you cannot override the private methods using metaprogramming, otherwise you could go ahead and change the
isValidTargetClassLoaderClass
method by doing this:However,
isValidTargetClassLoaderClass
is called byisValidTargetClassLoader
(another private method), which is called bychooseClassLoader
, which is a public method, which can be overridden using metaprogramming:All I did was replace any calls to
!isValidTargetClassLoader
withloader?.class == null
.Now, I am using Spock, so I put this code in my
setupSpec
method in my test class. However if you are using JUnit, I would imagine it would want to go in the method annotated with@BeforeClass
.Here is an example of it working with Spock (notice IntelliJ showing it about to return a class loader that would normally throw an exception: