`
weitao1026
  • 浏览: 1048825 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

使用Maven为一个项目生成多个Jar包,将一个目录打成jar包

 
阅读更多

今天又学了一招使用Maven为一个项目生成多个Jar包,或者说将某一个目录打成一个jar包。

 

虽然在Java平台下,各种构建工具如Maven、Gradle、SBT已经得到了较为广泛地运用,同时Maven约定的模块目录结构也得到了业界的认可,成为了Java平台下项目结构的事实标准。但我们总无法避免与各种遗留系统或老系统打交道。在没有Maven的时代,是Ant统治的时代,它因为提供了较为灵活的编写Task的功能,而忽略了制定一套看似呆板,实则有效的标准模板。于是,在不同的企业,不同的Java项目,出现了各种各样奇怪的目录结构与打包要求。拖着这些沉重的历史包袱,我们自然希望彻底革命,把那些看不顺眼的东西全部改造,让整个项目焕然一新。这是好事儿,所谓长痛不如短痛,一下子把问题肿瘤给割了,痛快!可是现实总有那么一些阻力会让我们缩手缩脚,我们不能挥起革命的利刃一阵乱砍,这弄不好会砍伤自己的手。于是乎,我们需要做战略性的撤退。退一步海阔天空嘛。

我面对的就是这样一个软件系统。这个Java开发的软件系统一直没有依赖管理,仅仅编写了Ant任务用于发布打包。我们的任务是渐进地引入Maven,并在从Build到deploy的整个生命周期中,逐步替换Ant,与持续集成搭配起来。这个系统的多数模块都划分了服务端与客户端。然而不巧的是,各个模块的服务端和客户端都集中在一个模块中。同时,这个项目的目录结构并非标准的Maven结构,如下图所示。因此,还需要自定义Source与TestSource的目录结构。在原来的Ant任务中,是将它们打包成了两个Jar包。现在,我们需要在Maven中同样做到这一点。

分析这个目录结构,无非是在打包时,对文件进行include或exclude。我查阅到Maven的一位开发者Tim O’Brien写的一篇博客 Sonatype的博客 ,详细介绍了具体的做法。当然,在博客中,他一再强调了这种做法的不可取,建议在项目模块上做出好的分解,保证一个Module对应一个Jar包。这篇博客介绍了两种做法,一个是在Profile中定义,一个则是在build中定义,使用的插件皆为maven-jar-plugin。对于我要解决的问题,可以考虑选择使用第二种做法,因为它只需要执行一条mvn package命令就能同时得到Server和Client的Jar包。具体的做法就是在插件的配置中,include各自的文件夹即可。配置如下:

    <groupId>com.test.maven</groupId>
    <artifactId>testmaven</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <testSourceDirectory>testSrc</testSourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <id>server</id>
                        <goals><goal>jar</goal></goals>
                        <phase>package</phase>
                        <configuration>
                            <classifier>server</classifier>
                            <includes>
                                <include>**/server/**</include>
                            </includes>
                        </configuration>
                    </execution>
                    <execution>
                        <id>client</id>
                        <goals><goal>jar</goal></goals>
                        <phase>package</phase>
                        <configuration>
                            <classifier>client</classifier>
                            <includes>
                                <include>**/client/**</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

通过这样的配置,运行mvn package可以生成三个包,其中testmaven-1.0-SNAPSHOT.jar同时包含了服务端和客户端的类;而服务端和客户端对应的Jar则为testmaven-1.0-SNAPSHOT-server.jar和testmaven-1.0-SNAPSHOT-client.jar。

在前面的配置中,我们并没有为server和client包定义自己的坐标,而是沿用了统一的一个。这就意味着依赖这个包的其他Module,可能无法通过Dependency来精确定位Server或Client。这对于部署来说,是没有问题的,但却无法进行依赖管理;除非在依赖的时候,去依赖整个大的模块。

要保证依赖管理,就意味着需要为server和client分别指定各自的坐标。看来需要另辟蹊径。其实,Maven是支持在一个项目中建立多个子模块的。我们可以考虑在项目中引入两个子模块,分别对应server和client,并在这两个子模块中建立自己的pom.xml文件。这在本质上是与Maven多模块支持是相同的,唯一不同的是代码结构。而且这种新建模块并没有影响原有的目录结构,对于遗留系统而言,还是可以接受的。因此,我们建立了如下图所示的模块结构:

在新的结构中,除了原有模块外,我还引入了另外两个新的模块server和client,它们除了拥有自己的pom.xml文件,没有其他任何内容。而在原有模块下,同样定义了一个pom.xml文件,它将作为整个项目的parent。定义如下:

    <groupId>com.test.maven</groupId>
    <artifactId>testmaven</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>server</module>
        <module>client</module>
    </modules>

这里定义的坐标是整个项目的坐标,同时指定了packaging的类型为pom。在这个pom.xml文件中还包括了两个子模块,其中的值应该与模块的名称对应。接下来配置server模块的pom.xml:

    <parent>
        <groupId>com.test.maven</groupId>
        <artifactId>testmaven</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>testmaven-server</artifactId>
    <build>
        <sourceDirectory>../src</sourceDirectory>
        <testSourceDirectory>../testSrc</testSourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <includes>
                        <include>**/server/**</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

首先声明了parent指向了主模块的坐标。接下来,声明当前模块的artifact id为testmaven-server。这就为server指定了独立的坐标。一旦部署后,在maven的Repository中会得到这样的文件:com/test/maven/testmaven-server/1.0-SNAPSHOT/testmaven-server-1.0-SNAPSHOT.jar。我们就可以在依赖中这样声明:

    <dependencies>
        <dependency>
            <groupId>com.test.maven</groupId>
            <artifactId>testmaven-server</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

运行mvn package,oops……竟然出现问题了。什么问题呢?单元测试无法通过。报告的错误为:

Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile (default-testCompile) on project testmaven-server: Compilation failure: Compilation failure:
[ERROR] /Users/twer/learn/testmaven/testSrc/com/test/maven/client/HelloMavenTest.java:[11,9] cannot find symbol
[ERROR] symbol  : class HelloMaven
[ERROR] location: class com.test.maven.client.HelloMavenTest

仔细分析,原来是在执行编译server包时,报告无法编译client包对应的测试类。怎么会在编译server包时,去编译client包对应的测试呢?仔细观察我们的pom.xml文件,在maven-compiler-plugin插件中,我们配置了对server文件的引入,这就意味着在编译server包时,不会引入client文件夹下的所有文件(当然在这里就是Java类文件)。但是,我们并没有在test-compile阶段排除client对应的测试文件。这就导致client的测试无法找到对应的实现类。找到根源,问题就好解决了,显然我们需要在test-compile阶段排除client文件夹。所以,server模块下正确的pom.xml配置为:

    <parent>
        <groupId>com.test.maven</groupId>
        <artifactId>testmaven</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>testmaven-server</artifactId>
    <build>
        <sourceDirectory>../src</sourceDirectory>
        <testSourceDirectory>../testSrc</testSourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <includes>
                        <include>**/server/**</include>
                    </includes>
                </configuration>
                <executions>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>test-compile</phase>
                        <configuration>
                            <testExcludes>
                                <exclude>**/client/**</exclude>
                            </testExcludes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

注意:我在IntelliJ配置execution下的configuration节时,碰到一个问题,那就是针对testExcludes配置节没有智能提示。由于其他maven配置节在正确情况下都有智能提示,因而让我产生错误,认为这个配置项不支持testExcludes,这让我纠结了好半天。

对于client模块而言,如法炮制,只是包含以及过滤的文件夹反转了一个个儿而已。当我们进行install甚至deploy时,在repository下的test/maven文件夹中,看到了三个文件夹,如图所示:

其中的testmaven/1.0-SNAPSHOT文件夹下并没有jar包,因为它对应的配置为主模块的配置,也就是parent配置。在这个配置中,我们将packaging的类型设置为pom了。

分享到:
评论

相关推荐

    如何生成jar包

    生成JAR包是Java项目打包的一种常见方式,它用于将多个.class文件及相关资源文件打包成一个文件,便于程序的分发和部署。JAR文件的全称是Java Archive,本质上是一个ZIP格式的压缩文件,包含了一个清单文件...

    【绝对可行,解决404】jfinal项目打包成jar在控制台运行方法

    本文将详细介绍一个非Maven项目的JFinal应用如何打包成JAR,并在控制台上运行。 首先,JFinal项目打包的关键在于确保所有依赖的库和配置文件都被包含在内。由于本项目不是通过Maven构建的,我们需要手动处理这些...

    使用Maven assembly打多个自定义包及War包简介

    Assembly插件是Maven生态系统中的一个组件,它的主要功能是将项目的所有依赖、资源和配置文件打包成不同的格式,如tar.gz、zip或jar等。 首先,我们需要在`pom.xml`文件中引入Maven Assembly插件。这可以通过在`...

    gradle将多模块打包成一个jar执行类demo

    标题中的“gradle将多模块打包成一个jar执行类demo”指的是使用Gradle构建工具将一个包含多个子模块的项目打包成一个可执行的JAR文件。在Java开发中,多模块项目通常是为了实现模块化设计,提高代码的可复用性和可...

    maven 3 jar 运行文件打包

    Maven会自动处理依赖关系,将所有依赖的库打包到一个或多个JAR文件中,或者创建一个包含所有依赖的“fat”或“uber”JAR。 5. **运行JAR**:生成的JAR文件可以在命令行中直接运行,例如:`java -jar target/hello...

    ant打jar包时, 怎么把所依赖的jar包与从源码中编译过来class文件合并成最终的一个jar?

    当我们有多个jar包相互依赖时,需要将它们合并成一个单一的jar,以便于部署和使用。Ant是一个流行的Java构建工具,它允许我们通过配置XML文件(build.xml)来执行自动化构建任务,包括jar文件的创建和合并。下面我们...

    springboot 多项目打包 jar包教程,包含源代码

    这些插件允许我们将多个jar合并成一个单一的jar,并解决可能的类冲突。 4. **处理类冲突** 当多个jar包含相同类时,可能会发生类冲突。使用`maven-shade-plugin`或`shadow`插件的重命名功能,可以将冲突的类重命名...

    maven打包成第三方jar包且把pom依赖包打入进来的方法

    当开发的项目需要打包成JAR文件并分发给其他项目使用时,通常需要将项目依赖的其他JAR包一同打包进去,这就涉及到如何将POM文件中定义的依赖一起打包成一个独立的第三方JAR包。本文将介绍如何利用Maven的相关插件和...

    项目进行打jar包(Fat)

    在IT行业中,构建可执行的Java应用程序通常涉及将多个库和依赖项整合到一个单一的JAR文件中,这个过程被称为“打Fat JAR”或“Uber JAR”。这种打包方式使得用户可以在不配置额外环境的情况下运行程序,因为它包含了...

    JAVA打成jar包

    如Maven的shade插件和Gradle的shadow插件,它们不仅能打包项目,还能合并多JAR文件为一个,处理依赖冲突。 9. **Spring Boot**: Spring Boot项目默认生成可执行的JAR,内置Tomcat服务器,直接运行即可启动应用。...

    java打包jar方案的优缺点分析及解决方案

    Java打包成JAR文件是将源代码编译后的字节码和相关资源组合在一起的常见方式,便于分发和执行。以下是对三种不同打包方法的详细分析和比较: 1. **使用Eclipse插件fat.jar打包** `fat.jar`插件允许开发者将所有...

    详解IDEA中MAVEN项目打JAR包的简单方法

    IDEA中MAVEN项目打JAR包的简单方法 IDEA 中的 Maven 项目可以使用简单的方法将项目打包成 ...使用 Maven 项目在 IDEA 中打包成 JAR 文件非常简单,只需要添加相应的插件和配置文件,就可以生成一个可执行的 JAR 文件。

    SpringBoot项目使用maven-assembly-plugin根据不同环境打包成tar.gz或者zip

    `maven-assembly-plugin`是Maven的一个插件,用于生成项目的归档文件,如tar.gz或zip,这在分发和部署软件时非常有用。本篇将详细介绍如何利用`maven-assembly-plugin`在Spring Boot项目中实现这个功能。 首先,`...

    fatjar工具 将java工程打成可执行jar包

    Java开发过程中,将一个工程打包成可执行的JAR(Java Archive)文件是常见的需求,尤其是对于命令行应用或者小型程序。"Fatjar"工具就是这样一个解决方案,它可以帮助开发者将所有的依赖库、类文件和资源文件整合到...

    maven的优缺点 项目

    将Service层以下的代码放到一个新创建的java项目中,在部署的时候将Service(java项目)打成一个jar包,分别放到两个web服务中;编译的时候让web项目依赖与Service(java项目)_source folder;配置麻烦,项目与项目之间的...

    java程序打jar包

    以下是对如何使用MyEclipse将Java程序打成jar包的详细步骤及注意事项的解释: 1. **项目导出**: 在MyEclipse中,首先需要选择你要打包的Java项目。右键点击项目,在弹出的菜单中选择“Export”(导出)选项,这是...

    java代码的方式对java源码进行编译并打包成jar

    可以将这些文件组织好,然后一起打包到JAR或者创建一个包含多个JAR和其他文件的ZIP包。 5. **发布和使用SDK**:生成的SDK包可以通过Maven或Gradle等构建工具发布到私有或公共仓库,供其他开发者依赖。用户只需在...

    解决idea使用maven编译正常但是运行项目时却提示很多jar包找不到的问题

    在使用IDEA和Maven进行项目开发时,IDEA会生成一个.iml文件,该文件中配置了项目的JDK lib路径。如果其他同事提交代码时把.iml文件也一起提交了,而该文件中的JDK lib路径与自己电脑中的该路径不一致时,就会出现jar...

    Idea打包jar包

    其中,“打包”功能是Idea的一个重要特性,能够方便地将项目编译成可执行的jar包或war包等,便于程序的分发和运行。 #### 一、为什么要使用Idea进行打包? 1. **便捷性**:通过Idea内置的功能可以直接完成项目的...

Global site tag (gtag.js) - Google Analytics