目标:
将依赖的第三方 jar 包打进去
方法:
maven-assembly-plugin
环境:
IDEA 2016.3
JDK 1.8
遇到的问题:
此处耗时 2 天时间,遇到过的坑:
1.修改完 pom.xml 后,不生效。
--改 pom.xml 后,代码不生效,是因为对 IDEA 工具不熟,在修改完 xml 后,需要点工具右下角的 import changes 或者直接点 auto-import 就可以一劳永逸了。
2.生成 jar 后,idea 可以执行,但是 java -jar 无法执行,报错 Exception in thread "main" java.lang.NoClassDefFoundError
如果修改 pom.xml 中的 mainClass 生效了,说不定是 mainClass 传入的不对,使用 mvn exec:java -Dexec.mainClass="com.delon.main.Test"可以尝试 main 方法是否正确。
如果想用编译 Test.java 文件,可以使用 mvn clean compile exec:java -Dexec.mainClass="com.delon.main.Test"
3.生成 jar 后,idea 可以执行,java -jar 也可以执行,但是缺少相关依赖,报错 Exception in thread "main" java.lang.NoClassDefFoundError: okhttp3/RequestBody
参考如下解决方式即可。
maven 构建 jar 包的步骤:
1.执行可执行的 class,代码内需要有入口 main 方法
2.通过 mvn package 来构建 jar 包
3.使用 java -jar test.jar 来执行 jar 包
一、包含依赖 jar 包
maven 的 pom.xml 配置文件
xml version="1.0" encoding="UTF-8"?>
4.0.0modelVersion>
<groupId>**com.xxx.delon**groupId>
<artifactId>**bugly**artifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.7version>
<scope>testscope>
dependency>
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>3.3.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.2.2version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<configuration>
<appendAssemblyId>falseappendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
<archive>
<manifest>
<mainClass>**com.xxx.uploadFile**mainClass>
manifest>
archive> ta configuration>
<executions>
<execution>
<id>make-assemblyid>
<phase>packagephase>
<goals>
<goal>assemblygoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
生成 jar 包,会生成在 target 目录下
mvn package
解压缩 bugly-1.0-SNAPSHOT.jar->META-INF->MANIFEST.MF
Manifest-Version: 1.0 Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: delon
Build-Jdk: 1.8.0_112
Main-Class: com.xxx.uploadFile
执行:
运行 jar
java -jar test.jar
也可以通过如下命令
mvn assembly:assembly
#跳过测试
mvn -Dmaven.test.skip=true assembly:assembly
注意:在执行这个命令之前,必须先配置 Maven 的环境变量,检查是否配置可通过命令: mvn -version
如果上面的命令成功执行,那么在项目路径的 target 文件下就会有两个 jar 文件,一个是有 jar 包依赖的,一个是没 jar 包依赖的。
二、不包含依赖 jar 包
如果不想包含依赖的 jar 包,可以把里面的代码替换成如下 code:
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-jar-pluginartifactId>
<configuration>
<archive>
<manifest>
<addClasspath>trueaddClasspath>
<classpathPrefix>lib/classpathPrefix>
<mainClass>com.xxx.uploadFilemainClass>
manifest>
archive>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-dependency-pluginartifactId>
<executions>
<execution>
<id>copyid>
<phase>packagephase>
<goals>
<goal>copy-dependenciesgoal>
goals>
<configuration>
<outputDirectory> ${project.build.directory}/lib outputDirectory>
configuration>
execution>
executions>
plugin>
三、只包含部分依赖 jar 包
如果想只包含部分依赖 jar 包
比如说,想做一个工具 jar 包,依赖公共 jar 和自己本地 jar 包,本地 jar 包需要解压成 class 打到 jar 包内,而依赖的公共 jar 包则不需要。
剔除公共 jar 包 可以用
的值的含义:
compile,缺省值,适用于所有阶段,会随着项目一起发布。
provided,类似 compile,期望 JDK、容器或使用者会提供这个依赖。如 servlet.jar。
runtime,只在运行时使用,如 JDBC 驱动,适用运行和测试阶段。
test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。
system,类似 provided,需要显式提供包含依赖的 jar,Maven 不会在 Repository 中查找它。
编译的时候采用 compile
log4jgroupId> log4jartifactId> 1.2.17version> compliescope> trueoptional> dependency>在用 package 打包的时候,改成 test,生成的 jar 包里就不会有该 jar 包的类了。
log4jgroupId> log4jartifactId> 1.2.17version> testscope> trueoptional> dependency>build 配置项,mainClass 为空因为不是可执行 jar。
maven-assembly-pluginartifactId> jar-with-dependenciesdescriptorRef> descriptorRefs> mainClass> manifest> archive> configuration> make-assemblyid> packagephase> singlegoal> goals> execution> executions> plugin> plugins> build>==高级
## 说明
非 web 项目中经常遇到需要将工程打包成一个可执行 jar 包(通过在命令行执行 java 命令进行启动)的情 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 况。
一个可执行的 jar 包,需要满足以下条件:
- 在 jar 包中的/META-INF/MANIFEST.MF 元数据文件中必须保护 Main-Class 启动入口类信息
- 项目的所有依赖都必须包含在 Classpath 中。即依赖必须都被描述 MANIFEST 于.MF 文件中的 Class-Path 下
Maven 中可以通过许多插件完成打包任务,如强大的 maven-assembly-plugin 等。具体使用方式,可以参见各插件的说明。
基础概念:maven 的生命周期与插件
Maven 将工程的构建过程分为不同的生命周期(LifeCycle),每个生命周期中又划分为不同的阶段(Phase)。
LifyCycle 之间互相独立,且没有明确的顺序关系,而每个 LifeCycle 中的 Phase 间则存在明确的顺序关系,且必须依序执行。
Maven 内置了三个 LifyCycle,如 default(build)构建,clean 清理, site 生成文档与站点。
以 default 为例,其内置的 phase 主要包含有: validate,compile,test,package,intergration-test,verify,install,deploy.这些 phase 在项目 build 时会依次执行 。
Maven 所定义的 LifeCycle 与 Phase 只是抽象的概念,不涉及具体的功能。而功能的实现则由插件(Plugin)负责。
一个 Plugin 可以实现多个目标(Goal), Goal 可以绑定在多个 Phase 上, 每个 Phase 下也可以包含多个 Goal。可以将 Phase 视为 Goal 的容器。
Goal 是 Maven 里面最小的任务单位,相关于 Ant 的 target。Goal 与 Goal 之间是互相独立的。单独执行某个 Goal 不会导致其他 Goal 被执行。
当我们对一个工程执行打包命令 mvn package 时, maven 将从 validate 阶段开始,一个阶段一个阶段(compile, test)的执行,直至到达 package 阶段。
在执行到 compile 阶段时,插件 maven-compiler-plugin 的 compile goal 会被执行,因为这个 goal 是绑定在 compile 阶段。
同理,当执行到 package 阶段时,插件 maven-dependency-plugin 与 maven-resources-plugin 的相关 goal 都会被执行。
工程打包示例
目录结构示例
project/
+ src/main/java/
+ com.some.package
+ src/main/resources/
- settings.properties
- applicationContext.xml
- startup.bat
- pom.xml
打包后的期望结果
target/
+ conf/
- settings.properties
- applicationContext.xml
+ lib/
- project.jar
- startup.bat
project.jar 中不包含配置文件。
pom 文件中打包相关的配置
<build>
<plugins>
<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<verbose />
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<!-- 项目依赖插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive> <!-- 表示是否不包含间接依赖的包 -->
<stripVersion>false</stripVersion> <!-- 去除版本信息 -->
</configuration>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- 拷贝项目依赖包到lib/目录下 -->
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
</configuration>
</execution>
</executions>
</plugin>
<!-- 项目资源插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<!-- 拷贝项目src/main/resources/下,除.bat以外的所有文件到conf/目录下 -->
<outputDirectory>${project.build.directory}/conf</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<excludes>
<exclude>*.bat</exclude>
</excludes>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-command</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<!-- 只拷贝项目src/main/resources/目录下的.bat文件到输出目录下 -->
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<includes>
<include>*.bat</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- 打包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<!-- 生成MANIFEST.MF的设置 -->
<manifest>
<!-- 为依赖包添加路径, 这些路径会写在MANIFEST文件的Class-Path下 -->
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<!-- jar启动入口类-->
<mainClass>com.some.package.some.class.Main</mainClass>
</manifest>
<manifestEntries>
<!-- 在Class-Path下添加配置文件的路径 -->
<Class-Path>conf/</Class-Path>
</manifestEntries>
</archive>
<includes>
<!-- 打jar包时,只打包class文件 -->
<include>**/*.class</include>
</includes>
</configuration>
</plugin>
</plugins>
<!-- 解决eclipse下maven插件兼容性问题 -->
<pluginManagement>
<plugins>
<!-- Ignore/Execute plugin execution -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<!-- copy-dependency plugin -->
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<versionRange>[1.0.0,)</versionRange>
<goals>
<goal>copy-dependencies</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
打包命令 mvn clean package
附录 1 启动命令 bat 文件编写
REM 关闭输出
@echo off
REM 设置启动时使用的jdk。如果不设置,则使用系统变量中设置的jdk
set path=../jdk.1.7.80/bin
set classpath=../jdk.1.7.80/jre/lib
REM 最基本的jar包启动命令,使用MANIFEST中的入口类启动
java -jar project.jar
REM 指定jar包的某个类作为入口启动
java -cp project.jar some.package.some.class.MyClass
REM 设置jvm参数并启动jar包
java -Xms256m -Xmx512m -jar project.jar
REM 开启输出
echo on
附录 2 maven 打包时的文件拷贝
通常打包外部资源文件时,都使用 maven-dependency-plugin
或是 maven-resources-plugin
插件。但是项目中遇见一个问题,在打包 jnotify 的动态链接库时,使用上面两个插件进行文件拷贝时,程序会无法识别到打包后 dll 文件。
多次尝试后使用了 maven-antrun-plugin
进行拷贝,问题得到解决。其拷贝配置如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>copy-native-libraries</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<echo message="copy native libraries" />
<copy todir="${project.build.directory}/lib">
<fileset dir="${basedir}/lib"></fileset>
</copy>
</target>
</configuration>
</execution>
</executions>
</plugin>
https://www.cnblogs.com/dzblog/p/6913809.html
https://www.jianshu.com/p/afb79650b606
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于