【说明】基于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>
<!–<mainClass>com.origin.ChattingServer</mainClass>–>
<!–<fork>true</fork>–>
<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结构:
2、打包
mvn clean package -Dmaven.test.skip=true
jar包基本结构
META-INF\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放置
三、其他
1、打包完整的jar包,只需以下一个插件即可
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
META-INF\MANIFEST.MF
Main-Class(org.springframework.boot.loader.JarLauncher)所需的文件在jar包org目录下,这个用处参考SpringBoot jar包启动的原理
四、拓展
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">
<!– 指定哪些需要打包 –>
<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>
启动示例
# 如果第三方依赖不在【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当前目录时,是有用处,否则没啥作用)
Exception in thread “main” java.lang.ClassNotFoundException: xxx.xxx.启动类 ↩︎