草庐IT

Maven打包所有依赖到一个可执行jar中

拾年一剑 2023-04-12 原文

文章目录

需求

普通的Maven Java项目(非Springboot项目),需要打包成一个jar包(包含所有的第三方依赖jar包),能够放在服务器上单独运行。

遇到的问题

找不到主类

大家都知道,如果使用常见的maven-jar-plugin打包,只能将自己项目里的源码编译打包,不会包含第三方的jar包。

如果该项目没有第三方依赖包,则可以通过maven-jar-plugin打包,直接执行打好的jar包(java -jar xxx.jar),可能会遇到找不到主类的情况,可以通过下面的方式解决:指定主类

<plugin>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.0.2</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <mainClass>com.xxx.AppMain</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

注意:

META-INF文件夹是MANIFEST.MF文件的主页。 此文件包含有关JAR内容的元数据。
JAR包中的/META-INF/MANIFEST.MF元数据文件必须包含Main-Class(主类)信息。(工程里的src/META-INF/MANIFEST.MF)
项目所有的依赖都必须在Classpath中,其可以通过 MANIFEST.MF 指定或者隐式设置。

打包时没有包含第三方依赖

如果该项目有第三方依赖包,通过上面的方式打包,是不会包含第三方依赖的,直接运行生成的jar包会出错(相关依赖不存在)。
想要打包时包含第三方依赖,又可以分两种情况:

情况1

打包成可执行jar文件,但是将所有依赖(包括外部依赖)单独打包到另外一个指定文件夹下,通过指定Class-Path的方式关联。

  1. 打包依赖到指定文件夹
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.10</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

关于Maven dependency plugin可以参考我的另外一篇博客,有详细介绍:

Maven dependency plugin使用

  1. 指定启动入口,并关联依赖
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <!-- 是否绑定依赖,将外部jar包依赖加入到classPath中 -->
                <addClasspath>true</addClasspath>
                <!-- 依赖前缀,与之前设置的文件夹路径要匹配 -->
                <classpathPrefix>lib/</classpathPrefix>
                <!-- 主函数的入口 -->
                <mainClass>com.xxx.AppMain</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

配置好后,可以通过下面的命令编译打包:
mvn clean pacakge -DskipTests=true

注意,复杂情况下,classpath需要在运行时指定,如 java -cp …

情况2

将整个工程打成一个可执行jar包,包含所有的依赖。

需要通过maven-assembly-plugin插件来打包,可以实现该需求。

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.xxx.AppMain</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

配置好后,可以通过下面的命令编译打包:
mvn assembly:assembly
执行成功后会在target文件夹下多出一个以-jar-with-dependencies结尾的jar包. 这个jar包就包含了项目所依赖的所有jar的class

扩展问题

不希望依赖的jar包变成class

可以通过修改插件的配置做到

  1. 在本地maven仓库里找到maven-assembly-plugin
cd ~/.m2/repository/org/apache/maven/plugins/maven-assembly-plugin/
  1. 进入打包时运行使用的版本里,如2.2-beta-5;
  2. 解压maven-assembly-plugin-2.2-beta-5.jar;
  3. 进入解压好的文件夹找到assemblies\jar-with-dependencies.xml,
    把里面的UNPACK改成FALSE,保存即可;
  4. 还原解压后的文件为jar包
    例如,在~/.m2/repository/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5 路径里,执行下面的命令
jar cvfm maven-assembly-plugin-2.2-beta-5.jar maven-assembly-plugin-2.2-beta-5/META-INF/MANIFEST.MF -C maven-assembly-plugin-2.2-beta-5 .
  1. 再次使用mvn assembly:assembly打包,编译好的以-jar-with-dependencies结尾的jar包. 这个jar包就包含了项目所依赖的所有jar文件,不再是class;

项目里多个主类,如何动态指定

可以通过自定义property属性,在执行maven命令时,动态指定来实现,配置如下:

例如,main.class 则为自定义的;

<properties>
    <main.class>com.xxx.AppMain</main.class>
</properties>

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>${main.class}</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

运行maven命令时,动态指定:

mvn -Dmain.class=com.xxx.AppMain2 assembly:assembly

有关Maven打包所有依赖到一个可执行jar中的更多相关文章

  1. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  4. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  5. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  8. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  9. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  10. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

随机推荐