jar分离打包以及运行

【说明】基于maven的spring项目

一、jar打包

1、插件依赖

<plugins>
   <plugin>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-maven-plugin</artifactId>
       <executions>
           <execution>
               <goals>
                   <goal>repackage</goal>
               </goals>
           </execution>
       </executions>
       <!--<configuration>
           &lt;!&ndash;<mainClass>com.origin.ChattingServer</mainClass>&ndash;&gt;
           &lt;!&ndash;<fork>true</fork>&ndash;&gt;
           <addResources>true</addResources>
           <outputDirectory>${project.build.directory}/jar</outputDirectory>
       </configuration>-->
       <configuration>
           <layout>ZIP</layout>
           <!-- 生成真正不包含lib包的jar -->
           <includes>
               <include>
                   <groupId>non-exists</groupId>
                   <artifactId>non-exists</artifactId>
               </include>
           </includes>
           <skip>true</skip>
       </configuration>
   </plugin>
   <!-- maven-jar-plugin、maven-dependency-plugin、maven-resources-plugin 打jar资源分离插件 -->
   <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-jar-plugin</artifactId>
       <configuration>
           <!-- 不打包资源文件(配置文件和依赖包分开) -->
           <excludes>
               <exclude>*.*</exclude>
               <exclude>static/**</exclude>
               <exclude>templates/**</exclude>
           </excludes>
           <archive>
               <!-- 配置MANIFEST.MF信息 -->
               <manifest>
                   <!-- 将依赖包添加到Class-Path中,否则找不到任何依赖jar包 -->
                   <addClasspath>true</addClasspath>
                   <!-- MANIFEST.MF 中 Class-Path 加入前缀,结合addClasspath为true使用 -->
                   <!-- 这个关系到加载外部依赖jar包的位置所在 -->
                   <classpathPrefix>lib/</classpathPrefix>
                   <!-- jar包不包含唯一版本标识 -->
                   <useUniqueVersions>false</useUniqueVersions>
                   <!--指定入口类:不指定运行出错【xxx.jar中没有主清单属性】 -->
                   <mainClass>com.origin.ChattingServer</mainClass>
               </manifest>
               <manifestEntries>
                   <!-- MANIFEST.MF 中 Class-Path 加入资源文件目录 -->
                   <!-- 这个关系到加载外部资源的位置所在 -->
                   <Class-Path>res/</Class-Path>
               </manifestEntries>
           </archive>
           <outputDirectory>${project.build.directory}</outputDirectory>
       </configuration>
   </plugin>
   <!-- 该插件的作用是用于复制依赖的jar包到指定的文件夹里 -->
   <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-dependency-plugin</artifactId>
       <executions>
           <execution>
               <id>copy-dependencies</id>
               <phase>package</phase>
               <goals>
                   <goal>copy-dependencies</goal>
               </goals>
               <configuration>
                   <outputDirectory>${project.build.directory}/lib/</outputDirectory>
                   <excludeTransitive>false</excludeTransitive>
                   <stripVersion>false</stripVersion>
                   <includeScope>runtime</includeScope>
               </configuration>
           </execution>
       </executions>
   </plugin>
   <!-- 该插件的作用是用于复制指定的文件 -->
   <plugin>
       <artifactId>maven-resources-plugin</artifactId>
       <executions>
           <execution>
               <id>copy-resources</id>
               <phase>package</phase>
               <goals>
                   <!-- 复制配置文件 -->
                   <goal>copy-resources</goal>
               </goals>
               <configuration>
                   <resources>
                       <resource>
                           <directory>src/main/resources</directory>
                           <!--<includes>
                               <include>*.*</include>
                               <include>static/**</include>
                               <include>templates/**</include>
                           </includes>-->
                       </resource>
                   </resources>
                   <outputDirectory>${project.build.directory}/res</outputDirectory>
               </configuration>
           </execution>
       </executions>
   </plugin>
</plugins>

还要指明打包方式

<packaging>jar</packaging>

对应pom.xml结构:
pom.xml结构

2、打包

mvn clean package -Dmaven.test.skip=true

jar包基本结构
jar结构
META-INF\MANIFEST.MF
MANIFEST.MF信息

二、jar运行

方式一:一般运行

java -jar xxx.jar

备注:已经分离的资源文件(res目录)、第三方依赖jar(lib目录)放置在xxx.jar同级目录即可

方式二:指定任意目录(如资源、jar)运行

java -Xbootclasspath/a:../temp/res/ -Djava.ext.dirs=../temp/lib/ -jar xxx.jar

备注:
1、-Xbootclasspath/a指定classpath(实际参考:jar运行设置classpath)。放置静态资源、配置等文件的目录。
2、-Djava.ext.dirs指定第三方jar包目录(实际参考:jar运行指定其他jar支持包)。
3、其他更好的方式未找到

备注
springboot运行jar包时,命令指定(如方式二)优先级最高。否则默认会读取以下几种目录并且优先级依次降低
a、与运行jar包同级的config目录
b、与运行jar包同级目录
c、运行jar包内部classpath同级的config目录
d、运行jar包内部classpath同级目录

注意
运行jar方式不一样,会导致:通过class获取(类.class.getResource("/").getPath(),不能直接在类成员变量进行赋值,应该放置到某个方法赋值/返回)运行根目录(说白了就是classpath的父目录路径)不同

情况一:方式一得到的根目录是与运行xxx.jar同级的res目录(取决于打包时指定的Class-Path)
情况二:方式二得到的根目录是-Xbootclasspath/a指定的目录。如果只指定-Djava.ext.dirs,而没有指定-Xbootclasspath/a的情况,具体路径是-Djava.ext.dirs指定目录下,第一个目录(升序情况下)作为classpath;-Djava.ext.dirs指定目录下没有任何目录(即只有文件),则回到【情况一】。

建议:-Djava.ext.dirs指定目录下不要存在任何目录(文件夹),并且所有第三方依赖jar都放置在该目录下。否则也不清楚会出现其他情况错误情况。一般情况下,还是采用打包时指定的classpath放置
指定的classpath目录

三、其他

1、打包完整的jar包,只需以下一个插件即可

<plugins>
   <plugin>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
</plugins>

完整jar包结构
META-INF\MANIFEST.MF
MANIFEST.MF
Main-Class(org.springframework.boot.loader.JarLauncher)所需的文件在jar包org目录下,这个用处参考SpringBoot jar包启动的原理
org.springframework.boot.loader.JarLauncher

四、拓展

1、另一种方式打包分离以及运行jar

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>copy-dependencies</id>
                <phase>package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                    <outputDirectory>target/lib</outputDirectory>
                    <excludeTransitive>false</excludeTransitive>
                    <stripVersion>false</stripVersion>
                    <includeScope>runtime</includeScope>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
            <excludes>
                <exclude>**/*.*</exclude>
                <exclude>static/**</exclude>
                <exclude>templates/**</exclude>
            </excludes>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <layout>ZIP</layout>
            <includes>
                <include>
                    <groupId>non-exists</groupId>
                    <artifactId>non-exists</artifactId>
                </include>
            </includes>
            <!-- 项目启动入口,即@SpringBootApplication注解的类 -->
            <mainClass>xxx.xxxx.启动类</mainClass>
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>repackage</goal>
                </goals>
                <configuration>
                    <classifier>classes</classifier>
                    <attach>false</attach>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>run</goal>
                </goals>
                <configuration>
                    <target>
                    	<!-- 声明变量 -->
                        <property name="target">${project.build.directory}</property>
                        <property name="dist">${target}/distribution</property>
                        <property name="dist-tmp">${dist}/tmp</property>
                        <property name="app-name">${project.build.finalName}</property>
                    	<!-- 创建文件夹 -->
                        <mkdir dir="${dist-tmp}" />
                    	<!-- 赋值目标jar -->
                        <copy file="${target}/${app-name}.jar" tofile="${dist-tmp}/${app-name}.jar" />
                    	<!-- 将目标jar解压 -->
                        <unzip src="${dist-tmp}/${app-name}.jar" dest="${dist-tmp}" />
                    	<!-- 删除目标jar -->
                        <delete file="${dist-tmp}/${app-name}.jar" />
                    	<!-- 打包 -->
                        <!--<zip destfile="${dist}/${app-name}-pages.jar">
                            &lt;!&ndash; 指定哪些需要打包 &ndash;&gt;
                            <zipfileset dir="${dist-tmp}/META-INF" prefix="META-INF" />
                            <zipfileset dir="${target}/classes/static" prefix="static" />
                            <zipfileset dir="${target}/classes/templates" prefix="templates" />
                        </zip>-->
                    	<!-- 移动文件 -->
                        <move file="${target}/${app-name}-classes.jar" todir="${dist}" />
                        <move todir="${dist}/3rd-lib">
                            <fileset dir="${target}/lib" />
                        </move>
                    	<!-- 删除临时目录及其文件 -->
                        <delete dir="${dist-tmp}" />
                    	<!-- 复制classpath所有文件 -->
                        <copy todir="${dist}">
                            <fileset dir="${target}/classes">
                                <include name="**/*.*" />
                            </fileset>
                        </copy>
                    </target>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

maven-antrun-plugin打包结构
META-INF\MANIFEST.MF
在这里插入图片描述

启动示例

# 如果第三方依赖不在【xxx-classes.jar】同目录下,即可使用多个路径指定
java -jar -Dloader.path=.,3rd-lib xxx-classes.jar
# 或者直接(第三方依赖在同级目录下)
java -jar -Dloader.path=. xxx-classes.jar

备注
获取运行根目录:类.class.getResource("/").getPath()
1、-Dloader.path如果指定当前目录,则运行时根目录当前父目录,否则为jar内部路径(xxx-classes.jar!/BOOT-INF/classes!/)。
2、项目class文件必须在-Dloader.path指定的任一目录下(不能再上一级或下一级目录,只能是同级目录),否则出现找不到主类1(即@SpringBootApplication注解的类)(原因不清楚)
一个具有注脚的文本。

缺点:未了解清楚备注2的原因,很明显的问题是jar中有项目的class文件,在外部也要有项目的class文件,而且还要在-Dloader.path指定的同级目录下,否则无法启动。感觉jar包中BOOT-INF目录下的class文件没有用处(实际是有用的,当-Dloader.path没有指定xxx-classes.jar当前目录时,是有用处,否则没啥作用)


  1. Exception in thread “main” java.lang.ClassNotFoundException: xxx.xxx.启动类 ↩︎

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值