scala (jar) 库的着色依赖

Shading dependencies of scala (jar) library

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

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

我怎样才能做到这一点?

谢谢!

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

Proguard can rename packages inside jar and obfuscate code. It is a bit complicated but you can achieve you goal with it. sbt-proguard 插件正在积极维护

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

maven-shade like plugin for SBT

更新:

来自版本 0.14.0 sbt-assembly 插件 似乎有 shading ability

您可以使用自己的 classloader 执行此操作。

class加载器:
编写一个 class 加载程序,使用重写从不同的 class 加载程序加载 class 文件。

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

我已经使用这个 teqnuiqe 创建了一个 classloader。 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:
在您的构建中,您将所有库和私有 classes 置于您选择的前缀下。在我的 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")
        }
    }
}