我们提供安全,免费的手游软件下载!
有一天螃蟹出门,不小心撞倒了泥鳅
泥鳅很生气地说:你是不是瞎啊!
螃蟹说:不是啊,我是螃蟹
maven-shade-plugin 官网已经介绍的很详细了,我给大家简单翻译一下
This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies.
这段话简明扼要的概述了 maven-shade-plugin 的功能
能够将项目连同其依赖,一并打包到一个
uber-jar
中
uber-jar 就是一个超级 jar,不仅包含我们的工程代码,还包括依赖的 jar,和
spring-boot-maven-plugin
类似
能够对依赖 jar 中的包名进行重命名
这个功能就有意思了,后面我们详说
maven-shade-plugin 必须和 Maven 构建生命周期的 package 阶段绑定,那么当 Maven 执行
mvn package
时会自动触发 maven-shade-plugin;使用很简单,在
pom.xml
添加该插件依赖即可
org.apache.maven.plugins
maven-shade-plugin
3.6.0
package
shade
phase
和
goal
按如上固定配置,
configuration
才是我们自由发挥的平台;有了基本了解后,我们再结合官方提供的
Examples
来看看 maven-shade-plugin 具体能干啥
假设我们有项目
maven-shade-plugin-demo
,其项目结构如下
如果不做任何剔除,可以按如下配置进行全打包
cn.hutool
hutool-all
5.8.26
org.apache.maven.plugins
maven-shade-plugin
3.6.0
package
shade
执行
mvn package
后,我们会看到两个包
maven-shade-plugin-demo-1.0-SNAPSHOT.jar
就是 uber-jar;解压可看其结构
不仅包括
package
、还包括各种配置文件、元文件,统统打包进 uber-jar;而
original-maven-shade-plugin-demo-1.0-SNAPSHOT.jar
则是不包括依赖 jar 的原始项目包;如果我们比较细心的话,会发现打包的时候告警了
意思是说 hutool jar 包中有
META-INF/MANIFEST.MF
,而
maven-shade-plugin-demo
打包成 jar 后也包含
META-INF/MANIFEST.MF
,两者重复了,只会将其中一个复制进 uber jar;默认情况下,是将我们项目的 jar 中的
META-INF/MANIFEST.MF
复制进 uber jar
那如果我们想保留 hutool 下的 MANIFEST.MF,而去掉 maven-shade-plugin-demo 中的 MANIFEST.MF,该如何处理呢?只需要微调下
configuration
com.qsl:maven-shade-plugin-demo
META-INF/*.MF
此时 uber jar 中的 MANIFEST.MF 就来自 hutool jar 了
回到前面的
configuration
配置,我们需要明白其每个子标签的含义
filter
:过滤器,可以配置多个
artifact
:复合标识符,用来匹配 jar,简单点说,就是匹配 jar 的
匹配规则
按 Maven 的坐标:groupId:artifactId[[:type]:classifier] 进行配置,
groupId:artifactId
必配,
[[:type]:classifier]
选配;支持通配符
*
和
?
,例如:
(相当于匹配上所有jar)
exclude
:排除项,也就是不会复制进 uber-jar;支持通配符配置
include
:包含项,也就是
只有
这些会被复制进 uber-jar;支持通配符配置
我们实战下,假设我们项目结构如下所示
configuration
配置如下
com.qsl:maven-shade-plugin-demo
com/qsl/test/**
com/qsl/Entry.class
cn.hutool:hutool-all
cn/hutool/Hutool.class
cn/hutool/json/**
执行
mvn package
后,uber-jar 内部结构你们能想到吗?我们来看看实际结果
是不是和跟你们想的一样?
除了手动指定
filter
外,此插件还支持自动移除项目中没有使用到的依赖类,以此来最小化 uber jar 的体积;configuration 配置如下
true
我们在
StringUtil
中引入 hutool 的 StrUtil(相当于项目依赖了 StrUtil)
package com.qsl.util;
import cn.hutool.core.util.StrUtil;
/**
* @author: 青石路
*/
public class StringUtil {
public static boolean isBlank(String str) {
return StrUtil.isBlank(str);
}
}
然后打包,uber-jar 内部结构如下所示
从 maven-shade-plugin 1.6 开始,
minimizeJar
会保留
filter
中
include
配置的类,但是要注意:
inlcude 默认会排除所有不在 include 配置中的类
这就会导致问题,我们来看个案例,我们引入
logback
依赖,但代码中未用到它,而我们又想将其下的 class 复制进 uber-jar,另外我们还想将 hutool 的
cn/hutool/json
包下的全部类都复制进 uber-jar,并且开启
minimizeJar
,是不是按如下配置?
cn.hutool
hutool-all
5.8.26
ch.qos.logback
logback-classic
1.3.14
org.apache.maven.plugins
maven-shade-plugin
3.6.0
package
shade
true
ch.qos.logback:logback-classic
**
cn.hutool:hutool-all
cn/hutool/json/**
打包后看 uber-jar 目录结构
hutool 的
core
包没有复制进来,这是因为我们对 hutool 配置了
include
,默认把最小依赖的
core
包给排除掉了,那怎么办呢?插件提供了配置
来处理此种情况,它会覆盖
include
默认排除行为
cn.hutool:hutool-all
false
cn/hutool/json/**
这样配置之后,既能包含 hutool 的 json 包,又能包含最小依赖的 core 包
false 通常配合true 来使用,不然
cn.hutool:hutool-all false cn/hutool/json/** 这么配置有何意义?
如果 uber-jar 被其他项目依赖,而我们的 uber-jar 又是保留了依赖 jar 的 class 的全类名,那么就可能类重复而导致类加载冲突;比如项目A依赖了我们的
maven-shade-plugin-demo
,还依赖了 B.jar,两个 jar 中都存在
cn.hutool.core.util.StrUtil.class
,但 api 完全不一样,根据
双亲委派模型
,只会成功加载其中某个
cn.hutool.core.util.StrUtil.class
,那么另一个的 api 则使用不了。为了解决这个问题,插件提供了重定位功能,通过创建 class 字节码的私有副本,按新配置的 package,打包进 uber-jar
我们来看个案例,假设我们只需要 hutool 的 core 包,将其下所有的 class 按
com.qsl.core
包打包进 uber-jar,可以按如下配置
cn.hutool
hutool-all
5.8.26
org.apache.maven.plugins
maven-shade-plugin
3.6.0
package
shade
cn.hutool.core
com.qsl.core
cn.hutool:hutool-all
cn/hutool/core/**
打包后 uber-jar 目录结构如下
我们来看下 uber-jar 中的 StringUtil.class
依赖的
StrUtil
也被正确调整了,是不是很牛皮?
此时项目A 依赖 B.jar 的同时,又依赖我们的
maven-shade-plugin-demo
,就不会类重名了(package 不一致了)
relocation
同样支持
exclude
和
include
cn.hutool.core
com.qsl.core
cn.hutool.core.util.ObjUtil
cn.hutool.core.date.**
cn.hutool.json
com.qsl.json
cn.hutool.json.JSONUtil
cn.hutool.json.xml.**
cn.hutool:hutool-all
cn/hutool/core/**
cn/hutool/json/**
此时 uber-jar 的目录结构是怎样的?你们自己去试!
前面已经介绍过,打包后会生成两个包
但
original
开头的那个明显不是按 Maven 坐标命名的,所以它是不能够
install
到本地或者远程仓库的;如果需要将两个 jar 都
install
到仓库中,那么就需要用到插件的
Attaching the Shaded Artifact
(生成附属包)功能
cn.hutool.core
com.qsl.core
cn.hutool.core.util.ObjUtil
cn.hutool.core.date.**
cn.hutool.json
com.qsl.json
cn.hutool.json.JSONUtil
cn.hutool.json.xml.**
cn.hutool:hutool-all
cn/hutool/core/**
cn/hutool/json/**
true
qsl
部署到仓库的 jar 如下
这个就比较简单了,我们直接看配置
com.qsl.Entry
如上配置会将
Main-Class
写进 uber-jar 的 MANIFEST.MF,还可以通过
manifestEntries
自定义属性
com.qsl.Entry
qsl
打包之后,uber-jar 的 MANIFEST.MF 内容如下
Resource Transformers 已经介绍的很详细了,我就不一一介绍了,挑几个个人认为比较重要的简单讲一下
合并
META-INF/services/
下的文件,并对文件中的 class 进行重定向;我们来看个例子,hutool 下有文件
cn.hutool.aop.proxy.ProxyFactory
我们也自定义一个
configuration 配置如下
cn.hutool.aop
com.qsl.aop
打包后,hutool 与 uber-jar 的 cn.hutool.aop.proxy.ProxyFactory 文件内容差异如下
如果不配置
ServicesResourceTransformer
,结果是怎样,你们自己去试
将多个同名文件的内容合并追加到一起(不配置的情况下会覆盖,最终文件内容只是其中某个文件的内容),configuration 配置如下
META-INF/spring.factories
打包后文件内容合并如下
XmlAppendingTransformer
、
ResourceBundleAppendingTransformer
功能类似,只是针对的文件内容格式略微有点特殊,就不演示了,你们自行去测试
回到我们的主题,如果我们项目依赖的 jar 中出现了同名的 class (包名和类名均相同),根据
双亲委派模型
,只会加载其中某一个 class,虽然两个 class 同名了,但功能完全不一样,另一个未被加载的 class 的功能则用不了,如果想同时使用这两个同名 class 的功能,我们该如何处理?
甲方扔给两个存在包名与类名均相同的Jar包,要在工程中同时使用怎么办?
文中给出了几种解决方案(注意看评论区),最高效最实用的当属
maven-shade-plugin
;假设我们项目依赖的 A.jar 和 B.jar 都存在
com.qsl.Hello.class
,我们可以新建一个项目,名字叫
qsl-a
,没有任何代码,仅仅依赖 A.jar,然后利用 maven-shade-plugin 的
Relocating Classes
功能对 A.jar 中存在重名的 class 进行重定向,例如
com.qsl
com.qsla
com.qsl.Hello
然后打包得到 uber jar(qsl-a.jar),项目依赖从 A.jar 更改成 qsl-a.jar,B.jar 依赖继续保留,那么项目中可用的 Hello.class 就包括
com.qsl.Hello(B.jar)
com.qsla.Hello(qsl-a.jar)
问题是不是就得到解决了?更实际的案例,敬请期待我下篇博客
项目原始jar
以及
项目依赖的所有jar
,而输出目标是
uber-jar
,所以 maven-shade-plugin 的规则对
项目原始jar
是无效的
minimizeJar
针对的只是
class
,其他类型的文件不受此约束
热门资讯