0

我开始从一个著名的 Java 构建系统切换到 Gradle 来构建我所有的项目,仅仅两个小时后,我已经能够毫无问题地发布我的一个项目的新版本——轻而易举.

但是现在我遇到了一个困难。简而言之,我需要复制这个 Maven 插件ServiceLoader的功能,它为启用的服务生成必要的文件。

简而言之:给定一个基类foo.bar.MyClass,它会生成一个名为的文件META-INF/services/foo.bar.MyClass,其内容是当前项目中实现该接口/扩展该基类的一组类。这样的文件看起来像:

com.mycompany.MyClassImpl
org.othercompany.MyClassImpl

为了做到这一点,它使用我不知道什么作为类加载器,加载Class对象com.myCompany.MyClassImpl或其他什么,并检查这个类是否实现了想要的接口。

我正在尝试在 Gradle 中做同样的事情。几个小时的谷歌搜索让我找到了这个插件,但在与它的作者讨论了一下之后,这个插件似乎能够合并这些文件,而不是创建它们。所以,我必须自己做……

而且我是 Gradle 和 Groovy 的真正初学者,这无济于事!build.gradle 这是我当前的代码,此处链接到完整;输出(我设法以某种方式获得;不能从干净的目录中工作)如下所示(请耐心等待......我使用 Java,我最终很高兴;Groovy 对我来说是全新的):

/*
 * TEST CODE
 */

final int CLASS_SUFFIX = ".class".length();
final URLClassLoader classLoader = this.class.classLoader;
// Where the classes are: OK
final File classesDir = sourceSets.main.output.classesDir;
final String basePath = classesDir.getCanonicalPath();

// Add them to the classloader: OK
classLoader.addURL(classesDir.toURI().toURL())

// Recurse over each file
classesDir.eachFileRecurse {
    // You "return" from a closure, you do not "continue"...
    if (!isPotentialClass(it))
        return;

    // Transform into a class name
    final String path = it.getAbsolutePath();
    final String name = path.substring(basePath.length() + 1);
    final String className = name.substring(0, name.length() - CLASS_SUFFIX)
        .replace('/', '.');

    // Try and load it
    try {
        classLoader.loadClass(className);
        println(className);
    } catch (NoClassDefFoundError ignored) {
        println("failed to load " + className + ": " + ignored);
    }
}

boolean isPotentialClass(final File file)
{
    return file.isFile() && file.name.endsWith(".class")
}

输出:

com.github.fge.msgsimple.InternalBundle
failed to load com.github.fge.msgsimple.bundle.MessageBundle: java.lang.NoClassDefFoundError: com/github/fge/Frozen
failed to load com.github.fge.msgsimple.bundle.MessageBundleBuilder: java.lang.NoClassDefFoundError: com/github/fge/Thawed
com.github.fge.msgsimple.bundle.PropertiesBundle$1
com.github.fge.msgsimple.bundle.PropertiesBundle
com.github.fge.msgsimple.provider.MessageSourceProvider
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$1
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$2
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$3
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider
com.github.fge.msgsimple.provider.MessageSourceLoader
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$1
com.github.fge.msgsimple.provider.StaticMessageSourceProvider
com.github.fge.msgsimple.source.MessageSource
com.github.fge.msgsimple.source.MapMessageSource$Builder
com.github.fge.msgsimple.source.MapMessageSource$1
com.github.fge.msgsimple.source.MapMessageSource
com.github.fge.msgsimple.source.PropertiesMessageSource
com.github.fge.msgsimple.locale.LocaleUtils
com.github.fge.msgsimple.serviceloader.MessageBundleFactory
com.github.fge.msgsimple.serviceloader.MessageBundleProvider
:compileJava UP-TO-DATE

问题出在前两行:Frozen并且Thawed在另一个项目中,该项目在编译类路径中,但不在我目前设法抓取的类路径中……因此,这些类甚至无法加载。

如何修改该代码以便获得完整的编译类路径?是我的第一个问题。第二个问题:我如何将该代码插入构建过程?

4

1 回答 1

1

这里有一些提示:

  • 创建一个新的URLClassLoader,而不是重用现有的。
  • sourceSets.main.compileClasspath用(这是一个Iterable<File>)而不是初始化类加载器classesDir
  • 将代码转换为 Gradle 任务类。有关更多信息,请参阅Gradle 用户指南中的“编写简单的任务类” 。

理想情况下,您应该使用 ASM 之类的库来分析代码,而不是使用类加载器。为避免由于内部引用不在编译类路径上的类而无法加载类的情况,您可能希望使用初始化类加载器sourceSets.main.runtimeClasspath

于 2013-06-22T19:53:21.543 回答