maven 打包可运行的 jar 包

本贴最后更新于 2301 天前,其中的信息可能已经时异事殊

目标:

将依赖的第三方 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

  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 282 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...