使用 Maven 打包项目 我们在 Idea 构建 Maven 工程时,在 Idea 的侧边栏可以调出 Maven 相关的操作选项,可以很容易的来执行生命周期,clean 可以清理编译出来的文件,compile 对文件进行编译,package 将项目进行打包,install 将打包完成的包发布到本地的 Maven 仓库。如果我们没有为各个生命周期配置插件,Maven 将使用默认的插件完成这些生命周期。例如,Maven 默认使用 maven-jar-plugin 对项目进行打包,打包生成的包只能作为一个依赖包使用,不能作为一个可执行包,要作为一个可执行包,需要配置 Main-Class
Maven 项目读取本地 Jar 包 要在本地项目中引入本地的 Jar 包,网上很多,这里就说说通过 Scope 引入本地包的方法。 在 pom.xml 中添加下面的 dependency
1 2 3 4 5 6 7 <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-lang3</artifactId > <version > 3.8</version > <scope > system</scope > <systemPath > ${basedir}/lib/commons-lang3-3.8.jar</systemPath > </dependency >
在项目中则需要在根目录下新建 lib 文件夹,然后将 jar 包置入
其实如果不想后续打包可能造成的麻烦,最好是 install 到本地仓库然后引入
使用 maven-jar-plugin 打包可执行文件 使用 maven-jar-plugin 打包出来的可执行 jar 是不包含依赖的,所以我们需要将依赖也打包出来,需要使用 dependency-plguin 的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <version > 3.0.2</version > <configuration > <archive > <manifest > <addClasspath > true</addClasspath > <classpathPrefix > lib/</classpathPrefix > <mainClass > Test</mainClass > </manifest > <manifestEntries > <Class-Path > lib/commons-lang3-3.8.jar</Class-Path > </manifestEntries > </archive > </configuration > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-dependency-plugin</artifactId > <version > 3.1.1</version > <executions > <execution > <id > copy-dependencies</id > <phase > package</phase > <goals > <goal > copy-dependencies</goal > </goals > <configuration > <outputDirectory > ${project.build.directory}/lib</outputDirectory > <overWriteReleases > false</overWriteReleases > <overWriteSnapshots > false</overWriteSnapshots > <overWriteIfNewer > true</overWriteIfNewer > </configuration > </execution > </executions > </plugin >
jar-plugin 的 指定了 classpath 的位置,本案例中即读取可执行包同级目录下的 lib 目录,可执行 jar 的 Manifest 文件如下。dependency-plugin 则将 maven 中的依赖包打包到 指定目录下。
1 2 3 4 5 6 7 8 9 Manifest-Version: 1.0 Built-By: akis Class-Path: lib/guava-26.0-jre.jar lib/jsr305-3.0.2.jar lib/checker-qu al-2.5.2.jar lib/error_prone_annotations-2.1.3.jar lib/j2objc-annotat ions-1.1.jar lib/animal-sniffer-annotations-1.14.jar lib/commons-lang 3-3.8.jar Created-By: Apache Maven 3.3.9 Build-Jdk: 1.8.0_172 Main-Class: Test
将依赖包打包到可执行包中 要将依赖包中打包到可执行包中,有很多插件能做到,这里介绍一下 shade-plugin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-shade-plugin</artifactId > <version > 3.1.1</version > <executions > <execution > <goals > <goal > shade</goal > </goals > <configuration > <transformers > <transformer implementation ="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" > <manifestEntries > <Main-Class > ${app.main.class}</Main-Class > <X-Compile-Source-JDK > ${maven.compiler.source}</X-Compile-Source-JDK > <X-Compile-Target-JDK > ${maven.compiler.target}</X-Compile-Target-JDK > </manifestEntries > </transformer > </transformers > </configuration > </execution > </executions > </plugin >
该插件能将依赖包导入可行性 jar 包,但是不会引入上文提到的本地引入的依赖包,如果想导入本地依赖的包到可执行 jar,可以使用 addjars-maven-plugin ,有兴趣的读者可以试试
当然使用 java -cp 命令而不是 java -jar 命令来执行主类的话,可以暂时解决外置依赖没打包进依赖包的问题
1 2 // java -cp xx.jar:lib/xx.jar packageName.MainClassName java -cp testpackage-1.0-SNAPSHOT.jar:lib/commons-lang3-3.8.jar Test
最近发现ManifestResourceTransformer
除了可以指定 MainClass
, 其实还可以指定 Manifest 文件里任意的属性,类的名字也隐含了这一点,所以如果利用 shade-plugin
的话,可以指定读取外置的依赖,只需要在 manifestEntries
中增加 Class-Path
属性,如下:
1 2 3 4 5 6 <manifestEntries > <Main-Class > ${app.main.class}</Main-Class > <X-Compile-Source-JDK > ${maven.compiler.source}</X-Compile-Source-JDK > <X-Compile-Target-JDK > ${maven.compiler.target}</X-Compile-Target-JDK > <Class-Path > lib/${planet-graph-lib}</Class-Path > </manifestEntries >
打包后,就可以用 java -jar 来运行了
springboot 项目打包 springboot 项目默认使用了 spring-boot-maven-plugin,默认会打包一个嵌入了 tomcat 的可执行 jar 包,最近做的一个项目里就需要引入外置的 jar 包,并且不将其打包到 springboot 的可执行 jar 里,所以可以用上文提到的引入方法。这样如果我们的外置 jar 更新了,只需要替换掉外置的这个 jar,已经打包的应用则不需要更改。 那么需要解决的问题是在运行 springboot 打包出来的可执行 jar 的时候能够读到外置的这个 jar 依赖。
首先给出解决方案,配置 spring-boot-maven-plugin,将其 layout 改为 ZIP
1 2 3 4 5 6 7 8 9 <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <layout > ZIP</layout > <executable > true</executable > </configuration > </plugin >
然后在运行的时候加上 -Dloader.path 这个参数,lib 是 springboot 应用同级目录下的外置依赖目录,我们的外置依赖就放在这个 lib 中
1 java -Dloader.path=lib ph_pg-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
原因是 springboot 应用启动时时从 org.springframework.boot.loader.JarLauncher 开始启动的,该启动器会忽略外置的 classpath 下的包,因此需要替换成 PropertiesLauncher,配置 layout 为 ZIP 后,springboot 就从 PropertiesLauncher 开始启动,并且使用 loader.path 配置外置的 classpath
参考: StackOverflow Springboot Reference SpringBoot加载外部依赖 Springboot的“fat jar” 变成 “thin jar” , 这篇很值得看下
Maven 打包时排除掉某个文件 项目里 License 模块,LicenseGenerator 也在模块里,但是打包的时候我们就不能把这个文件打包到依赖包里了,解决方法是,打包时,让这个文件不参与编译就行了,如下,需要注意的是其他的 java 类里也不要 import 这个 LicenseGenerator,不然还是会被编译。我们及时清除不需要的 import 是个好习惯
1 2 3 4 5 6 7 8 9 10 11 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <configuration > <source > 1.8</source > <target > 1.8</target > <excludes > <exclude > path/to/license/LicenseGenerator.java</exclude > </excludes > </configuration > </plugin >
小结 闲来无事,记录下碰到的 Maven 打包的问题吧