【问题标题】:Shading dependencies of scala (jar) libraryscala (jar) 库的着色依赖
【发布时间】:2015-10-28 17:52:37
【问题描述】:

我想分发一个我创建的库的 jar,其中捆绑了我所有的依赖项。但是我想避免依赖项与采用项目的版本冲突。

我认为 maven shade 可以做到这一点,但我找不到使用 Scala / SBT 的方法。我发现 OneJar 但是从我的实验中它似乎只适用于可执行文件。

我怎样才能做到这一点?

谢谢!

【问题讨论】:

  • from version 0.14.0 sbt-assembly plugin 可以做到shading

标签: java scala jar sbt packaging


【解决方案1】:

您可以使用自己的类加载器来完成此操作。

类加载器:
编写一个类加载器,它使用重写从不同的类加载器加载类文件。

例如,您可以在获取资源时添加库作为类路径的前缀。

我已经使用这个 teqnuiqe 创建了一个类加载器。 https://github.com/espenbrekke/dependent/blob/master/src/main/java/no/dependent/hacks/PathRewritingClassLoader.java

它将 URLClassLoader 中的 findClass 方法替换为添加前缀的方法。

protected Class<?> findClass(final String name) throws ClassNotFoundException {
    Class result;
    try {
        result = (Class)AccessController.doPrivileged(new PrivilegedExceptionAction() {
            public Class<?> run() throws ClassNotFoundException {

// This is where the prefix is added:
                String path = PathRewritingClassLoader.this.prefix + name.replace('.', '/').concat(".class");
                Resource res = PathRewritingClassLoader.this._ucp.getResource(path, false);
                if(res != null) {
                    try {
                        return PathRewritingClassLoader.this._defineClass(name, res);
                    } catch (IOException var4) {
                        throw new ClassNotFoundException(name, var4);
                    }
                } else {
                    return null;
                }
            }
        }, this._acc);
    } catch (PrivilegedActionException var4) {
        throw (ClassNotFoundException)var4.getException();
    }

    if(result == null) {
        throw new ClassNotFoundException(name);
    } else {
        return result;
    }
}

我们还要重写资源加载

@Override
public URL getResource(String name){
    return super.getResource(prefix+name);
}

它的使用方法如下:

_dependentClassLoader = new PathRewritingClassLoader("private", (URLClassLoader)DependentFactory.class.getClassLoader());
Class myImplementationClass=_dependentClassLoader.loadClass("my.hidden.Implementation");

构建你的 jar:
在您的构建中,您将所有库和私有类放在您选择的前缀下。在我的 gradle 构建中,我有一个简单的循环来收集所有依赖项。

task packageImplementation {
dependsOn cleanImplementationClasses

doLast {
    def paths = project.configurations.runtime.asPath
    paths.split(':').each { dependencyJar ->
        println "unpacking" + dependencyJar

        ant.unzip(src: dependencyJar,
                dest: "build/classes/main/private/",
                overwrite: "true")
        }
    }
}

【讨论】:

  • 听起来不错,希望有一个包装系统可以做到这一点,但似乎没有。谢谢。完成后很高兴看到您的解决方案。
  • 刚刚更新了一些源示例和我当前实现的链接。希望它有所帮助:)
  • 甜心,有机会我会看看这个。谢谢。另外,如果您碰巧知道使用 sbt 而不是 gradle 进行构建的正确方法,我可以使用它:) 谢谢 Espen
  • 我添加了资源加载的重写,因为它破坏了我使用的一些库。似乎现在可以工作了。我对 sbt 没有任何经验,所以我无法为您提供帮助。
【解决方案2】:

Proguard 可以重命名 jar 中的包和混淆代码。这有点复杂,但你可以用它来实现你的目标。 sbt-proguard 插件正在积极维护中

您也可以查看类似主题的答案:

maven-shade like plugin for SBT

更新:

从版本0.14.0 sbt-assembly plugin 似乎有shading ability

【讨论】:

  • 虽然这似乎可行。如果我必须有一天我会试一试,这似乎是一个很大的黑客。谢谢
【解决方案3】:

你试过sbt-assembly插件吗?它有一套在发生冲突时的合并策略,并且有非常好的入门指南。

【讨论】:

  • sbt 程序集很棒,我使用它,但合并策略没有帮助,因为我的 jar 的 ussr 将如何合并东西不在我的控制之下,而且还因为即使通过解决显式定义合并规则,然后在 rubtime 使用的版本将与我的编译时版本不匹配。另外 sbt 程序集有一个选项可以从 jar 中排除依赖项,但我希望它们包括在内,只是隐藏。
猜你喜欢
  • 1970-01-01
  • 2011-08-08
  • 2014-07-26
  • 2015-10-27
  • 2017-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多