关于java:Executable-JarUber-JarShade-JarShadow-JarFat-Jar-到底是什么东西

在Java里,如果要运行一个Java程序,那么须要一个蕴含main办法Class的Jar包或者Class文件,而后执行命令

# 运行一个class文件
java $class

# 运行一个Java包(Manifest中指定了Main-Class)
java -jar $jarfile

# 运行一个Class的main办法,并且将多个jar包增加到运行时的classpath
java -cp(-classpath) $path(目录/jar文件/zip文件) #zip文件须要合乎jar格局的标准

这种形式会有一些弊病,因为大多数的Java程序里,会蕴含很多依赖,如果要启动这个程序就必须要通过-classpath来指定这些依赖包文件,而这些classpath的指定还必须得在服务器下来指定,不是很不便

尤其是在DevOps/微服务的流行下,这种指定classpath的形式显得太不灵便了,能不能做到间接构建一个聚合的jar包,执行公布或者运行呢?

Executable Jar

Executable Jar(可执行的jar包),个别是指将所有依赖的jar包都放在一个Jar包内,这个Jar包蕴含了所有运行时须要依赖的Jar包代码,不过并没有规定这个“放”的形式,能够是将依赖的jar包间接放在入口jar包内,就像这样

├─executable jar
│  ├─META-INF
│  │  ├─MANIFEST.MF
│  ├─com...
│  ├─lib
│  │  ├─io.netty....jar
│  │  ├─com.google....jar
│  │  ├─com.github....jar
│  │  ├─org.apache.....jar

也能够是将依赖的jar包内的文件给拷贝到入口的Jar包内,就像这样:

├─executable jar
│  ├─META-INF
│  │  ├─MANIFEST.MF
│  ├─com...
│  ├─io.netty.... classes
│  ├─com.google.. classes
│  ├─com.github.. classes
│  ├─org.apache.. classes

Spring Boot 算是把 Executable Jar传到了千家万户,Spring Boot中提供了一个Maven插件spring-boot-maven-plugin,这个插件能够将你所有的Maven依赖Jar包在构建时打包到一个jar文件内,并且通过Spring Boot的ClassLoader和启动类,能够加载到这些Executable Jar包中的Jar包,就是下面介绍的第一种形式:将依赖的jar包间接放在入口jar包内

Uber Jar

第一次看到这个词的时候,一头雾水,不晓得这个单词是个什么鬼意思,Uber打车?

在查阅材料后才晓得,Uber Jar的原单词是Über Jar,是德语单词,能够解释为”Over”,完结的意思,不过在理论的上下文中,翻译为“所有”可能更适合。

这个术语最后是由开发人员发明的,他们认为将所有依赖项和本人的代码放入一个jar文件中能够解决很多抵触问题。然而大多数输入法上德语Ü很难打进去,所以就成了”Uber “。

Fat jar

Fat 翻译为肥的、胖的、大的,Fat jar就很好解释了:一个“瘦削的” jar,和Uber jar表白的含意,指的是蕴含所有依赖包的Jar包

Shade Jar/Shadow Jar

shade机翻为暗影、遮蔽,shade jar是指将jar包及其依赖包打包到一个jar文件内,同时提供shade“遮蔽/重命名”某些依赖包的性能

比方一个Maven工程,依赖了很多三方包,但理论打包时你想重命名局部包,这个重命名的过程在这里能够叫shade

这里具体解释下“shade”,shade在这个过程里的目标时重命名某些包,那么为什么要重命名呢?

比方咱们基于JAVA Instrumentation API去开发一个Agent,咱们在开发这个Agent时也须要依赖一些三方包,比方Netty,那么在理论运行时须要通过-javaagent或者动静attach agent jar的模式去加载咱们这个Agent jar包;

这里加载的Agent只能是一个独立的jar包,所以首先要将咱们的agent和依赖的包都打到一个jar包内,构建一个”Uber jar”;而后须要思考Class包抵触的问题,因为Agent内的依赖包的Class,和指标JVM过程中的Class很可能是抵触的,比方Agent中依赖了netty 4.1.58.Final ,而指标JVM过程中依赖了netty 4.0.14.Final,而咱们的Agent中应用了4.0.14中不存在的API(比方某个类的办法,是新版本新增的);此时程序就会出错了,NoSuchMethodError,因为指标过程曾经加载了那个Class,Agent包中的同名Class不会被反复加载

这里解决这种抵触问题的话,有一个简略的计划,在构建”Uber jar”时,将依赖的包的包名批改,重定位(relocaiton),比方将io.netty批改为com.github.kongwu.io.netty,同时将Java代码中所有的援用都批改为重定位之后的包名。这样,通过批改包名,就完满避开了依赖包Class抵触的问题

而下面这个“重定位”的行为,就叫Shade,或者叫Shadow

Maven中的Shade 插件为https://maven.apache.org/plugins/maven-shade-plugin/,能够将程序打包成一个独立的Jar包,蕴含依赖包

另一款相似的Maven插件为https://maven.apache.org/plugins/maven-assembly-plugin/usage.html,也能够实现同样的成果

Gradle中也有相似的插件https://imperceptiblethoughts.com/shadow/,性能也很弱小,也反对shade性能

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理