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
  1. 当项目依赖其他jar包的时候,打出的jar包执行出错,抛出标题中的异常。

    原因:因为依赖jar包中的META-INF中有多余的.SF文件与当前jar包冲突。

  2. 解决办法:在打包的配置中排除 .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(全限定名)

 

posted @ 2023-06-19 13:51  夏之夜  阅读(73)  评论(0编辑  收藏  举报