“打包“这个词听起来比較土。比較正式的说法应该是”构建项目软件包“。详细说就是将项目中的各种文件,比方源代码、编译生成的字节码、配置文件、文档,依照规范的格式生成归档,最常见的当然就是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. Struts2+Spring+Hibernate实现员工管理增删改查功能(一)之登录功能

    昨天的博客中我分享了个人关于ssh实现员工管理的框架整合,今天我在分享管理员登录功能的实现.  转载请注明出处"http://www.cnblogs.com/smfx1314/p/78013 ...

  2. Scrum Meeting Alpha - 7

    Scrum Meeting Alpha - 7 NewTeam 2017/11/1 地点:新主楼F座二楼 任务反馈 团队成员 完成任务 计划任务 安万贺 登录部分的API仍存在问题 完成登陆部分的AP ...

  3. 【OpenCV】通过ROI区域以及掩码实现图像叠加

    在图像处理领域,我们常常需要设置感兴趣区域(ROI,region of interest),来专注或者简化我们的工作过程 .也就是从图像中选择的一个图像区域,这个区域是我们图像分析所关注的重点.我们圈 ...

  4. Java设计模式相关面试

    1.接口是什么?为什么要使用接口而不是直接使用具体类? 接口用于定义 API.它定义了类必须得遵循的规则.同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以使 ...

  5. MSSQL-并发控制-1-Transaction

         MSSQL并发控制原先打算分为两个部分写:隔离级别及锁,写的过程中,发现需要提及下事务的相关内容,故加多一篇博文,共3篇.         如果转载,请注明博文来源: www.cnblogs ...

  6. Js实现子窗体给父窗体的元素赋值

    假设父窗体有一个html标签元素的id为myCollectionWork,现在如果我想在子窗体中给这个元素赋值那么我们可以使用如下JS代码:$("#myCollectionWork" ...

  7. 手 Q 人脸识别动画实现详解

    欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 前言 开门见山,先来看下效果吧. 看到这么酷炫的效果图,不得不赞叹一下我们的设计师.然而,站在程序员的角度上看,除了酷炫之外更多的是复杂.但是 ...

  8. HTML Entity 字符实体

    目录 1. HTML Entity 2. 字符与Entity Name的互相转换 3. 字符与Entity Number的互相转换 1. HTML Entity 1.1 介绍 在编写HTML页面时,需 ...

  9. 苹果快速的修复了Mac OS High Sierra 上出现了root的漏洞

    最近苹果因为Mac最新系统 Mac OS High Sierra 上出现了root的漏洞走上了风口浪尖,不过还好,在一封苹果给科技媒体'9to5 Mac'的回复中得知,苹果在接收到报告之后,立即展开修 ...

  10. 校验ISBN的方法

     国际标准书号(International Standard Book Number,ISBN:是国际通用的图书或独立的出版物(除定期出版的期刊)代码.出版社可以通过国际标准书号清晰的辨认所有非期刊书 ...