Maven打包方式
1、背景描述
Maven可以使用mvn package
指令对项目进行打包,如果使用java -jar
执行jar
文件,会出现没有设置Main-Class、ClassNotFoundException(找不到依赖包)等错误
要想jar
包能直接通过java -jar
运行,需要满足:
- 在
jar
包中的META-INF/MANIFEST.MF中指定Main-Class,以确定程序入口 - 要能加载到
jar
包中所依赖的其它依赖包
使用Maven有以下几种方式可以生成能直接运行的jar
包:
2、使用maven-jar-plugin和maven-dependency-plugin插件打包
maven-jar-plugin
和maven-dependency-plugin
是默认的打包方式,用来将普通的Project打成jar
包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<!-- 是否添加依赖的jar路径配置 -->
<addClasspath>true</addClasspath>
<!-- 依赖的jar包存放未知,和生成的jar放在同一级目录下 -->
<classpathPrefix>lib/</classpathPrefix>
<!-- 指定入口类 -->
<mainClass>com.cc.Main</mainClass>
</manifest>
</archive>
<!-- 不打包com.cc.excludes下面的所有类 -->
<excludes>com/cc/excludes/*</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
可以使用includes
或excludes
选择只打包的部分内容
maven-jar-plugin
用于生成META-INF/MANIFEST.MF文件的部分内容:
<mainClass>com.cc.Main</mainClass>
:指定META-INF/MANIFEST.MF中的Main-Class<addClasspath>true</addClasspath>
:在META-INF/MANIFEST.MF中加上Class-Path并配置依赖包<classpathPrefix>lib/</classpathPrefix>
:指定依赖包所在的目录
例如,下面是一个通过maven-jar-plugin
插件生成的MANIFEST.MF文件片段:
Class-Path: lib/commons-logging-1.2.jar lib/commons-io-2.4.jar
Main-Class: com.cc.Main
只是生成MANIFEST.MF文件还不够,maven-dependency-plugin
插件用于将依赖包拷贝到<outputDirectory>${project.build.directory}/lib</outputDirectory>
指定的位置,即lib
目录下
配置完成后,通过mvn package
指令打包,会在target
目录下生成jar
包,并将依赖包拷贝到target/lib
目录下:
target
lib
commons-logging-1.2.jar
commons-io-2.4.jar
test.jar
指定了Main-Class,有了依赖包,那么就可以直接通过java -jar
运行jar
包
详见官方文档:https://maven.apache.org/shared/maven-archiver/examples/classpath.html
这种方式生成jar
包有个缺点,就是生成的jar
包太多不便于管理,下面两种方式只生成一个jar
文件,包含项目本身的代码、资源以及所需的依赖包
3、使用maven-assembly-plugin插件打包
maven-assembly-plugin
是使用最多的打包方式,可以自定义打包结构,也可以定制依赖项等
例如,大数据项目中往往有很多Shell脚本、SQL脚本、Properties及XML配置等,使用maven-assembly-plugin
插件可以让输出的结构清晰并标准化
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<archive>
<manifest>
<mainClass>com.cc.Main</mainClass>
</manifest>
</archive>
<!-- 使用Maven预配置的描述符 -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
打包方式为:mvn package assembly:single
assembly:single
是Maven插件的一个目标(goal
),用于创建一个包含所有依赖的单一可执行jar
文件。当执行mvn assembly:single
命令时,Maven会使用maven-assembly-plugin
来创建一个包含项目所有依赖的jar
文件,这个jar
文件可以直接运行而不需要额外的依赖库
打包结果会在target
目录下生成一个xx-jar-with-dependencies.jar
的jar
文件,这个文件不但包含了自己项目中的代码和资源,还包含了所有依赖包的内容。所以可以直接通过java -jar
来运行
此外还可以直接通过mvn package
来打包,无需assembly:single
,只需要额外添加一些配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<archive>
<manifest>
<mainClass>com.cc.Main</mainClass>
</manifest>
</archive>
<!-- 使用Maven预配置的描述符 -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!-- 绑定到package生命周期 -->
<phase>package</phase>
<goals>
<!-- 只运行一次 -->
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
其中<phase>package</phase>
和<goal>single</goal>
即表示在执行mvn package
打包时使用assembly:single
方式,所以可以直接使用mvn package
打包
值得注意的是,如果项目中使用到Spring Framework,用该方式打出来的jar
包运行时会报错,使用如下方式可以处理
4、使用maven-shade-plugin插件打包
maven-shade-plugin
用来打可执行的jar
包,也就是所谓的fat jar
包
maven-shade-plugin
插件提供了两大基本功能:一是将依赖jar
包打包到当前jar
包,二是对依赖的jar
包进行重命名,以用于类的隔离
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.cc.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
配置完成后,执行mvn package
即可打包。在target
目录下会生成两个jar
包,注意不是original-xx.jar
文件,而是另外一个。与maven-assembly-plugin
相同,生成的jar
文件包含了所有依赖,所以可以直接运行
如果项目中使用了Spring Framework,将依赖打到一个jar
包中,运行时会出现读取XML Schema文件出错。原因是Spring Framework的多个jar
包中包含相同的文件spring.handlers
和spring.schemas
,如果生成一个jar
包会互相覆盖。为了避免互相影响,可以使用AppendingTransformer来对文件内容追加合并:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.cc.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
详见官方文档:https://maven.apache.org/plugins/maven-shade-plugin/examples/attached-artifact.html
5、Maven自定义打包方式
Maven自定义打包方式示例如下:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 解决资源文件编码问题 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 设置编译版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<skip>true</skip>
</configuration>
</plugin>
<!-- 打包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<mainClass>com.cc.Main</mainClass>
</manifest>
</archive>
<!-- 使用Maven预配置的描述符 -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!-- 绑定到package生命周期 -->
<phase>package</phase>
<goals>
<!-- 只运行一次 -->
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
其中,我们也可以配置描述符文件自定义打包内容,详情参考文末参考文章
<configuration>
<!-- 配置描述符文件 -->
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
参考文章:
https://cloud.tencent.com/developer/article/2089955
https://developer.aliyun.com/article/1378456