Java 程序打包教学
一、第三方jar包和可执行jar包分开打
1、目的
自研的程序多个第三方jar包,在程序投入生产运行的过程中,随着时间推移,第三方jar包会包爆出安全漏洞,此时只需要针对第三方jar包进行升级,为了快速升级以及减小补丁包的大小和减少影响范围,现程序打包时,需要将依赖的所有第三方jar包放在lib目录下,自研的程序是一个可执行程序。
2、实现
1)maven-jar-plugin + maven-dependency-plugin 两个插件配合打出包
a) 配置
<plugin> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <!-- 告知 maven-jar-plugin添加一个 Class-Path元素到 MANIFEST.MF文件,以及在Class-Path元素中包括所有依赖项 --> <addClasspath>true</addClasspath> <!-- 所有的依赖项应该位于 lib文件夹 ,lib目录和程序的jar包需要在同级目录下 --> <classpathPrefix>lib/</classpathPrefix> <!-- 当用户使用 lib命令执行JAR文件时,使用该元素定义将要执行的类名 --> <mainClass>com.test.WebApplication</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>prepare-package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <!-- ${project.build.directory}为Maven内置变量,缺省为target --> <outputDirectory>${project.build.directory}/lib</outputDirectory> <!-- 表示是否不包含间接依赖的包 --> <excludeTransitive>false</excludeTransitive> <!-- 表示复制的jar文件去掉版本信息 --> <stripVersion>false</stripVersion> </configuration> </execution> </executions> </plugin>
b) 效果
- 打出来的包格式
lib 目录 和 程序对应的jar(示例是test.jar)都在target目录下自动生成。
-
程序运行
$JAVA -Xdebug -Xrunjdwp:transport=dt_socket,address=10.19.230.60:1088,server=y,suspend=n -Xmx100m -Xms100m -Dlogging.path=$HOME/logs -Dserver.port=$managerPort -Dhttpserver.second.port=$webPort -DcomponentPath=$componentPath -jar $HOME/bin/manager/test.jar &
将程序放到环境上,使用 java 的 -jar命令可以直接运行
maven-dependency-plugin的详细用法参见:maven pom 配置 学习笔记(六)之 maven-dependency-plugin (依赖zip包并将其解压到某个目录下) - 夏之夜 - 博客园 (cnblogs.com)
maven-jar-plugin的详细用法参见:maven pom 配置 学习笔记(五)之 maven-jar-plugin - 夏之夜 - 博客园 (cnblogs.com)
2) maven-jar-plugin + maven-assembly-plugin 两个插件配合打出包
a) 配置
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <!-- 执行的主程序路径 --> <mainClass>com.test.WebApplication</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> #利用插件 maven-assembly-plugin 将依赖的第三方jar包全部打进lib目录下,而本程序对应的jar包放在lib下的同级目录下 <configuration> <!--修复 打包 没有主清单属性的时候 --> <finalName>test</finalName> <appendAssemblyId>false</appendAssemblyId> <outputDirectory>../output/</outputDirectory> #放到与工程根目录的同级目录下。 如果 这里值填写成output 则表示 放在 工程根目录下 <descriptors> <descriptor>src/assembly/assembly.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
<?xml version="1.0" encoding="UTF-8"?> <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> <id>build</id> <formats> <format>zip</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <fileSets> <fileSet> <directory>${project.build.directory}</directory> #将target编译目录下的test.jar(自己程序编译出来的jar包) 放到 zip的根目录下 <outputDirectory>/</outputDirectory> <includes> <include>test.jar</include> </includes> <fileMode>0755</fileMode> </fileSet> </fileSets> <dependencySets> <dependencySet> <useProjectArtifact>false</useProjectArtifact> <outputDirectory>/lib</outputDirectory> #将第三方jar包全部放到 zip的根目录/lib 下 <scope>runtime</scope> <directoryMode>0755</directoryMode> </dependencySet> </dependencySets> </assembly>
b) 效果
c) 程序运行命令
$JAVA -Xdebug -Xrunjdwp:transport=dt_socket,address=10.19.230.60:1088,server=y,suspend=y -Xmx100m -Xms100m -Dlogging.path=$HOME/logs -Dserver.port=$managerPort -Dhttpserver.second.port=$webPort -DcomponentPath=$componentPath -classpath $HOME/bin/manager/test.jar:$HOME/bin/manager/lib/* com.test.WebApplication &
二、打包成一个jar包且能够指定运行的主类
1、目的
(1) 依赖的jar包以及本工程可以打包到一个jar包中;
(2) 本java工程中存在多个主类,不同的主类提供的作用不同,现要求 第三方使用这个 jar包的时候可以根据其实际情况 调用该jar包中不同的主类来获取不同的信息或完成不同的任务。
2、实现
(1) maven-assembly-plugin(其中包含自己依赖的jar包)
1)简单配置打包,将所有依赖的第三方jar包包括自己的class文件均打进最终的jar包中
a) 配置
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.5.5</version> <configuration> <archive> <manifest> <mainClass>com.demo.DemoMain</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin>
b) 打包命令
maven 命令: clean package assembly:single
b) 效果
打出来的一个jar包,其中目录结构大概如下:
2)配置打包,依赖的多个第三方jar包使用了spi方式 在各自的jar包中 META-INF/services 下配置了同个接口的多个实现类。此时打进最终的jar包中META/INF/services下的一个接口文件中应该合并所有jar包的实现类声明
A.jar
|---- META-INF/services/com.interface.testA 该文件的内容有:com.testA.aa 以及 com.testA.ab
B.jar
|----- META-INF/services/com.interface.testA 该文件的内容有:com.testA.ba 以及 com.testA.bb
a) 配置
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <!-- 这里要修改 , 指定程序入口主类 --> <mainClass>com.test.WebApplication</mainClass> </manifest> </archive> <descriptors> <descriptor>src/assembly/assembly.xml</descriptor> #该文件的内容和 maven-assembly-plugin 插件自带的id为 jar-with-dependencies的descriptor.xml内容一致 ,目的都是把第三方jar包打进一个jar包中 </descriptors> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
<?xml version="1.0" encoding="UTF-8"?> <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> <id>jar-with-dependencies</id> <formats> <format>jar</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <outputDirectory>/</outputDirectory> <unpack>true</unpack> </dependencySet> </dependencySets> <containerDescriptorHandlers> <containerDescriptorHandler> <handlerName>metaInf-services</handlerName> </containerDescriptorHandler> </containerDescriptorHandlers> #新增的内容,表示合并所有jar包中的 的 META-INF/services下同名文件中的所有内容 </assembly>
b) 打包命令
maven 命令: clean package assembly:single
c) 效果
最终打出一个名为 c-jar-with-dependencies.jar 包
c-jar-with-dependencies.jar
|------ META-INF
|------ services
|------- com.interface.testA
文件com.interface.testA内容为:
com.testA.aa
com.testA.ab
com.testA.ba
com.testA.bb
maven-assembly-plugin 插件的其它打包配置可详见:maven pom 配置 学习笔记(一)之 maven-assembly-plugin 进行打包 - 夏之夜 - 博客园 (cnblogs.com)
(2) maven-shade-plugin
a) 配置
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.5.1</version> <configuration> <createDependencyReducedPom>true</createDependencyReducedPom> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <!-- <minimizeJar>true</minimizeJar> --> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.hikvision.clusterWeb.WebApplication</mainClass> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> </transformers> </configuration> </execution> </executions> </plugin>
b) 效果
该打包出来的目录结构与 maven-assembly-plugin打出来的结构差不多,但是对于用到SPI的多个jar包,maven-shade-plugin 更加方便,不需要额外配置 xml 去描述,仅需要增加配置: <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />就可以了。
C) 问题
- 解决java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
-
当项目依赖其他jar包的时候,打出的jar包执行出错,抛出标题中的异常。
原因:因为依赖jar包中的META-INF中有多余的.SF文件与当前jar包冲突。
- 解决办法:在打包的配置中排除 .SF 等文件
<filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters>
(3) 指定运行 jar包中的某个 主类
test.jar 包中 有个 A、B 两个主类
a) 指定执行 主类 A
java -cp test.jar A(全限定名)
如: java -cp test.jar com.demo.A
b) 指定执行主类B
java -cp test.jar B(全限定名)