“打包“这个词听起来比較土。比較正式的说法应该是”构建项目软件包“。详细说就是将项目中的各种文件,比方源代码、编译生成的字节码、配置文件、文档,依照规范的格式生成归档,最常见的当然就是JAR包和WAR包了,复杂点的样例是Maven官方下载页面的分发包。它有自己定义的格式,方便用户直接解压后就在命令行使用。

作为一款”打包工具“,Maven自然有义务帮助用户创建各种各样的包。规范的JAR包和WAR包自然不再话下。稍微复杂的自己定义打包格式也必须支持。本文就介绍一些经常使用的打包案例以及相关的实现方式,除了前面提到的一些包以外。你还能看到怎样生成源代码包、Javadoc包、以及从命令行可直接执行的CLI包。

Packaging的含义

不论什么一个Maven项目都须要定义POM元素packaging(假设不写则默认值为jar)。顾名思义,该元素决定了项目的打包方式。实际的情形中。假设你不声明该元素。Maven会帮你生成一个JAR包。假设你定义该元素的值为war。那你会得到一个WAR包。假设定义其值为POM(比方是一个父模块),那什么包都不会生成。

除此之外,Maven默认还支持一些其它的流行打包格式,比如ejb3和ear。你不须要了解详细的打包细节。你所须要做的就是告诉Maven,”我是个什么类型的项目“。这就是约定优于配置的力量。

为了更好的理解Maven的默认打包方式。我们最好还是来看看简单的声明背后发生了什么。对一个jar项目运行mvn package操作,会看到例如以下的输出:

[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ git-demo ---
[INFO] Building jar: /home/juven/git_juven/git-demo/target/git-demo-1.2-SNAPSHOT.jar

相比之下。对一个war项目运行mvn package操作,输出是这种:

[INFO] --- maven-war-plugin:2.1:war (default-war) @ webapp-demo ---
[INFO] Packaging webapp
[INFO] Assembling webapp [webapp-demo] in [/home/juven/git_juven/webapp-demo/target/webapp-demo-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/home/juven/git_juven/webapp-demo/src/main/webapp]
[INFO] Webapp assembled in [90 msecs]
[INFO] Building war: /home/juven/git_juven/webapp-demo/target/webapp-demo-1.0-SNAPSHOT.war

相应于相同的package生命周期阶段,Maven为jar项目调用了maven-jar-plugin。为war项目调用了maven-war-plugin,换言之。packaging直接影响Maven的构建生命周期。了解这一点很重要,特别是当你须要自己定义打包行为的时候,你就必须知道去配置哪个插件。一个常见的样例就是在打包war项目的时候排除某些web资源文件,这时就应该配置maven-war-plugin例如以下:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<webResources>
<resource>
<directory>src/main/webapp</directory>
<excludes>
<exclude>**/*.jpg</exclude>
</excludes>
</resource>
</webResources>
</configuration>
</plugin>

源代码包和Javadoc包

本专栏的《坐标规划》一文中曾解释过,一个Maven项目仅仅生成一个主构件,当须要生成其它附属构件的时候,就须要用上classifier。源代码包和Javadoc包就是附属构件的极佳样例。

它们有着广泛的用途,尤其是源代码包。当你使用一个第三方依赖的时候,有时候会希望在IDE中直接进入该依赖的源代码查看事实上现的细节,假设该依赖将源代码包公布到了Maven仓库。那么像Eclipse就能通过m2eclipse插件解析下载源代码包并关联到你的项目中。十分方便。因为生成源代码包是极其常见的需求,因此Maven官方提供了一个插件来帮助用户完毕这个任务:

  <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>

类似的,生成Javadoc包仅仅须要配置插件例如以下:

  <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

为了帮助全部Maven用户更方便的使用Maven中央库中海量的资源,中央仓库的维护者强制要求开源项目提交构件的时候同一时候提供源代码包和Javadoc包。这是个非常好的实践,读者也能够尝试在自己所处的公司内部实行。以促进不同项目之间的交流。

可运行CLI包

除了前面提到了常规JAR包、WAR包。源代码包和Javadoc包,还有一种常被用到的包是在命令行可直接执行的CLI(Command Line)包。默认Maven生成的JAR包仅仅包括了编译生成的.class文件和项目资源文件,而要得到一个能够直接在命令行通过java命令执行的JAR文件。还要满足两个条件:

  • JAR包中的/META-INF/MANIFEST.MF元数据文件必须包括Main-Class信息。
  • 项目全部的依赖都必须在Classpath中。

Maven有好几个插件能帮助用户完毕上述任务,只是用起来最方便的还是maven-shade-plugin。它能够让用户配置Main-Class的值,然后在打包的时候将值填入/META-INF/MANIFEST.MF文件。关于项目的依赖,它非常聪明地将依赖JAR文件所有解压后。再将得到的.class文件连同当前项目的.class文件一起合并到终于的CLI包中,这样,在运行CLI
JAR文件的时候。全部须要的类就都在Classpath中了。以下是一个配置例子:

  <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.juvenxu.mavenbook.HelloWorldCli</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>

上述样例中的。我的Main-Class是com.juvenxu.mavenbook.HelloWorldCli,构建完毕后。相应于一个常规的hello-world-1.0.jar文件,我还得到了一个hello-world-1.0-cli.jar文件。细心的读者可能已经注意到了,这里用的是cli这个classifier。最后,我能够通过java -jar hello-world-1.0-cli.jar命令执行程序。

自己定义格式包

实际的软件项目经常会有更复杂的打包需求,比如我们可能须要为客户提供一份产品的分发包,这个包不只包括项目的字节码文件,还得包括依赖以及相关脚本文件以方便客户解压后就能执行,此外分发包还得包括一些必要的文档。这时项目的源代码文件夹结构大致是这种:

pom.xml
src/main/java/
src/main/resources/
src/test/java/
src/test/resources/
src/main/scripts/
src/main/assembly/
README.txt

除了主要的pom.xml和一般Maven文件夹之外,这里另一个src/main/scripts/文件夹,该文件夹会包括一些脚本文件如run.sh和run.bat。src/main/assembly/会包括一个assembly.xml,这是打包的描写叙述文件,稍后介绍。最后的README.txt是份简单的文档。

我们希望终于生成一个zip格式的分发包,它包括例如以下的一个结构:

bin/
lib/
README.txt

当中bin/文件夹包括了可运行脚本run.sh和run.bat,lib/文件夹包括了项目JAR包和全部依赖JAR,README.txt就是前面提到的文档。

描写叙述清楚需求后,我们就要搬出Maven最强大的打包插件:maven-assembly-plugin。它支持各种打包文件格式。包含zip、tar.gz、tar.bz2等等,通过一个打包描写叙述文件(该例中是src/main/assembly.xml)。它可以帮助用户选择详细打包哪些文件集合、依赖、模块、和甚至本地仓库文件,每一个项的详细打包路径用户也能自由控制。例如以下就是相应上述需求的打包描写叙述文件src/main/assembly.xml:

<assembly>
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<outputDirectory>/</outputDirectory>
<includes>
<include>README.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/scripts</directory>
<outputDirectory>/bin</outputDirectory>
<includes>
<include>run.sh</include>
<include>run.bat</include>
</includes>
</fileSet>
</fileSets>
</assembly>
  • 首先这个assembly.xml文件的id相应了其终于生成文件的classifier。
  • 其次formats定义打包生成的文件格式,这里是zip。

    因此结合id我们会得到一个名为hello-world-1.0-bin.zip的文件。(如果artifactId为hello-world,version为1.0)

  • dependencySets用来定义选择依赖并定义终于打包到什么文件夹,这里我们声明的一个depenencySet默认包括全部全部依赖,而useProjectArtifact表示将项目本身生成的构件也包括在内,终于打包至输出包内的lib路径下(由outputDirectory指定)。
  • fileSets同意用户通过文件或文件夹的粒度来控制打包。

    这里的第一个fileSet打包README.txt文件至包的根文件夹下,第二个fileSet则将src/main/scripts下的run.sh和run.bat文件打包至输出包的bin文件夹下。

打包描写叙述文件所支持的配置远超出本文所能覆盖的范围,为了避免读者被过多细节扰乱思维,这里不再展开,读者若有须要能够去參考这份文档

最后,我们须要配置maven-assembly-plugin使用打包描写叙述文件,并绑定生命周期阶段使其自己主动运行打包操作:

  <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

执行mvn clean package之后,我们就能在target/文件夹下得到名为hello-world-1.0-bin.zip的分发包了。

小结

打包是项目构建最重要的组成部分之中的一个。本文介绍了主流Maven打包技巧,包含默认打包方式的原理、怎样制作源代码包和Javadoc包、怎样制作命令行可执行的CLI包、以及进一步的。怎样基于个性化需求自己定义打包格式。这当中涉及了非常多的Maven插件。当然最重要,也是最为复杂和强大的打包插件就是maven-assembly-plugin。其实Maven本身的分发包就是通过maven-assembly-plugin制作的,感兴趣的读者能够直接查看源代码一窥到底。

原文地址:http://www.infoq.com/cn/news/2011/06/xxb-maven-9-package

Maven实战(九)——打包的技巧的更多相关文章

  1. 学习笔记——Maven实战(九)打包的技巧

    “打包“这个词听起来比较土,比较正式的说法应该是”构建项目软件包“,具体说就是将项目中的各种文件,比如源代码.编译生成的字节码.配置文件.文档,按照规范的格式生成归档,最常见的当然就是JAR包和WAR ...

  2. 《maven实战》笔记(2)----一个简单maven项目的搭建,测试和打包

    参照<maven实战>在本地创建对应的基本项目helloworld,在本地完成后项目结构如下: 可以看到maven项目的骨架:src/main/java(javaz主代码)src/test ...

  3. 【转】Maven实战(九)---模块聚合和继承

    原博文出自于:http://blog.csdn.net/liutengteng130/article/details/47001831   感谢! 类之间有聚合和继承关系,Maven也具备这样的设计原 ...

  4. Maven实战(九)---模块聚合和继承

    类之间有聚合和继承关系,Maven也具备这种设计原则. 那么Maven的pom是怎样进行聚合与继承的呢? 一.什么是聚合?为什么要用聚合? 上一篇博客介绍了模块化的基本知识. 有了模块化,那么我们项目 ...

  5. [maven] 实战笔记 - 构建、打包和安装maven

    ① 手工构建自己的maven项目 Maven 项目的核心是 pom.xml.POM (Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等 ...

  6. Maven实战:Maven生命周期

    前言 之前有写过一篇文章Maven实战,介绍了Maven的一些基本概念,以及对于一个初学者而言的Maven基础知识,当时在我看来掌握了这些基本是够用的. 随着工作的深入,越来越感觉对于Maven的理解 ...

  7. maven实战(01)_搭建开发环境

    一 下载maven 在maven官网上可下载maven:http://maven.apache.org/download.cgi 下载好后,解压.我的解压到了:D:\maven\apache-mave ...

  8. 学习笔记——Maven实战(三)多模块项目的POM重构

    重复,还是重复 程序员应该有狗一般的嗅觉,要能嗅到重复这一最常见的坏味道,不管重复披着怎样的外衣,一旦发现,都应该毫不留情地彻底地将其干掉.不要因为POM不是产品代码而纵容重复在这里发酵,例如这样一段 ...

  9. 学习笔记——Maven实战(六)Gradle,构建工具的未来?

    Maven面临的挑战 软件行业新旧交替的速度之快往往令人咂舌,不用多少时间,你就会发现曾经大红大紫的技术已经成为了昨日黄花,当然,Maven也不会例外.虽然目前它基本上是Java构建的事实标准,但我们 ...

随机推荐

  1. linux中安装Python3.x

    首先了解几句Linux命令是必须的.例如 ls, vi, wget, rm, mv, cd, su, sudo, chmod, tar等等一些常用的语句命令是有必要知道它的用法的. 安装Python3 ...

  2. ORM框架SQLAlchemy与权限管理系统的数据库设计

    SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用对象关系映射进行数据库操作,即:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果. 执行流 ...

  3. 阿里云全民云计算活动:云服务器ECS二折起(云主机)采购指南

    首先要注册并登录阿里云,完成实名认证 可以用手机号新注册账号, 也可以使用淘宝账号直接登录,其他的登录方式还支持微博账号和支付宝账号等. 登录后如下图,先点"控制台", 然后鼠标移 ...

  4. 虚拟机通信配置与Xshell连接

    本文主要讲解虚拟机通信配置的详细步骤和Xshell工具连接,以及如何诊断网络问题并进行相应配置的问题. 1. 虚拟机通信配置 虚拟机通信配置的基本流程如图所示: 首先,我们先打开新建的虚拟机,然后输入 ...

  5. Python 直接赋值、浅拷贝和深度拷贝解析

    直接赋值:其实就是对象的引用(别名). 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象. 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象 ...

  6. 堆排序HeapSort

    堆排序,顾名思义,是采用数据结构堆来进行排序的一种排序算法. 研究没有规律的堆,没有任何意义.特殊的堆有最大堆(父节点值大于等于左右字节点值),最小堆(父节点值小于等于子节点值).一般采用最大堆来进行 ...

  7. mysql数据库第一弹

    mysql(一) sql语句 sql是Structured Query Language(结构化查询语言)的缩写.SQL是专为数据库而建立的操作命令集,是一种功能齐全的数据库语言. 在使用它时,只需要 ...

  8. Request 和 Response 区别

    Request 和 Response 对象起到了服务器与客户机之间的信息传递作用.Request 对象用于接收客户端浏览器提交的数据,而 Response 对象的功能则是将服务器端的数据发送到客户端浏 ...

  9. 史上最强学生管理系统之ArrayList版

    其实不管是网上或者培训班,都会有学生管理系统的最基础版本,本人也不过是照猫画虎,在某些细节方面进行了一些渲染,使这个最基本的小程序更加人性化和便于利于操作一点,个人愚见,大牛勿喷,欢迎转载(请注明出处 ...

  10. Ajax跨域 CROS处理

    Ajax跨域方法有多种 这里介绍CROS跨域的实际案例 场景:A域名 请求 B域名: 暂且 A为客户端 B为服务端: 请求的服务端必须自己能控制 或者服务器端头部已经添加 Access-Control ...