写个JVM构建工具相关的吧(总是要写点什么的)

Ant、Maven、Gradle的东西在笔记里放了好久了,现在拿出来写写。


在两只狼和一只狗的时代。

人们编译和运行还是靠着重复敲打着gcc和javac,这显然是件不够”优雅”的事情。于是,上帝说,要有光,世界便有了光——make出来。

make帮助人们批量的编译文件,或者说是构建工程。

而作为一个主要写Java的程序员,也有JVM生态圈中的构建工具。

主要是3个Ant、Maven、Gradle

Ant

Apache Ant是一款基于Java的构建工具,Java实现,也意味着有很好的跨平台性。开始是转为tomcat设计的,使用xml,比makefile更加简洁,后被广泛应用。

1.安装与配置

在这里找到相应的压缩包,下载解压就好了,然后配置环境变量。没什么不同的。当然,之前也得配好jdk。

之后呼出cmd,输入ant -version。可显示版本号就可以了。

2.构建ant实例

让我们脱离ide,用回最原始的javac和java。

例如这里

1
2
3
4
cd workspace
md anttest & cd anttest
md src & md bin &cd src #src 放置源代码,bin放置.class文件
notepad HelloAnt.java

之后随便写点

1
2
3
4
5
public class HelloAnt {
public static void main(String[] args) {
System.out.println("Hello Ant!");
}
}

紧接着

1
2
3
#在项目路径 anttest下
javac -sourcepath src -d bin src\HelloAnt.java #两个参数是指明源代码路径和.class文件路径 最后是编译的目标文件
java -cp bin HelloAnt #-cp即 classpath 执行

之后就可以看到Hello Ant了。(啊,上面似乎是废话。。

也尝试了使用直接打包成jar,不过还要手动写mainfest,这里不写了,直接用ant。

ant中核心的就是build.xml文件

build.xml的结构如下:

img

允许有且只有一个project元素,其中有多个target,每个target作为task的集合存在。

下面定义了四个target:

– clean 清除编译文件以及相关的目录

– compile 编译src中的文件 并放入指定的classpath

– jar 打包成jar

– run 运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<project name="anttest" default="run" basedir=".">
<description>Building Simple Java Applications Without An IDE</description>
<target name="clean" description="clean class file">
<delete dir ="exe"/>
</target>
<!--编译的target,执行编译命令-->
<target name="compile"
description="Compile the Java code.">
<mkdir dir ="bin"/>
<javac srcdir="src"
destdir="bin"
debug="true"
includeantruntime="false" />
</target>
<!--打包的命令,依赖于compile的target,即需要执行compile的-->
<target name ="jar" depends="compile"
description="to jar">
<mkdir dir ="exe"/>
<jar destfile = "exe/HelloAnt.jar" basedir ="bin">
<manifest>
<attribute name = "Main-Class" value = "HelloAnt"/>
</manifest>
</jar>
</target>
<target name="run" depends="clean,compile,jar"
description="Run the Java application.">
<java classname="HelloAnt" fork="true">
<classpath>
<pathelement path="bin"/>
</classpath>
</java>
</project>

target是自定义的,不是说一定会有这几个name,想要执行某一条target,可以键入

1
2
3
ant clean
ant run
ant #默认执行build.xml中name为run的target

img

xml中的元素,清晰明了,每一个target中定义了实现的命令。

xml中的元素,清晰明了,每一个target中定义了实现的命令。

关于这些标签可以去这里了解。

通过上面这个例子进行了简单项目的简单构建,在感受到简单清晰之后,也发现了一个问题,那就是随着模块的增加,bulid.xml会变得很大。

这也是ant一个很明显的缺点。

Ivy是Apache另外一个开源项目,主要解决构建中的依赖问题。他可以和Ant完美集成,所以常常一起使用,这里也放在Ant下面进行讨论。

上面的例子是个很简单的例子,也不需要有外部的依赖,如果需要用到外部的依赖可以使用ivy。

这里下载之后解压,把解压之后的ivy-x.x.x.jar文件copy到ant路径下的lib中就可以了。(也可以配置环境变来那个IVY_HOME

之后可以去ivy的路径下,去看ivy给出的示例。

在%IVY_HOME%\src\example\hello-ivy的位置执行ant,进行构建,这里有ivy.xml和build.xml。

img

可以看到从外部下载所依赖的文件,这些文件被缓存在用户.ivy2\cache目录下。

1
2
3
4
5
6
7
8
<!--ivy.xml-->
<ivy-module version="2.0">
<info organisation="org.apache" module="hello-ivy"/>
<dependencies>
<dependency org="commons-lang" name="commons-lang" rev="2.0"/>
<dependency org="commons-cli" name="commons-cli" rev="1.0"/>
</dependencies>
</ivy-module>

而在ant的build.xml中定义ivy任务实现调用ivy的功能,从而实现依赖管理的。只列出 调用ivy的功能

1
2
3
4
5
6
7
8
9
10
11
<target name="resolve" description="--> retreive dependencies with ivy">
<ivy:retrieve/>
</target>
<target name="report" depends="resolve" description="--> generates a report of dependencies">
<ivy:report todir="${build.dir}"/>
</target>
<target name="clean-cache" description="--> clean the ivy cache">
<ivy:cleancache />
</target>

只需要在build.xml中引入ivy的空间声明就可以。

3.eclipse和IntelliJ IDEA中也有相关的插件

….略了啊


Maven

maven,是在ant之后Apache又发布的另一个构建工具,旨在解决ant使用中的一些问题。不过maven还是使用了xml文件,当然,和ant相比,改变了很多。ant更像是把构建项目中的每一个任务都写下来,每个任务中进行哪些命令也都写进去。那么maven关心什么呢?

1 依赖的管理:仅仅通过jar包的几个属性,就能确定唯一的jar包,在指定的文件pom.xml中,只要写入这些依赖属性,就会自动下载并管理jar包。

2 项目的构建:内置很多的插件与生命周期,支持多种任务,比如校验、编译、测试、打包、部署、发布…

3 项目的知识管理:管理项目相关的其他内容,比如开发者信息,版本等等

1.安装配置环境

还是直接上手,下载配置环境。

2.构建简单的工程

1 mvn archetype:create -DgroupId=com.company.example -DartifactId=Example

这里的archetype是项目的模型,maven自身提供了一些项目模型,也可以通过archetype:generate进行选择。

groupId和artifactId分别是公司组织名和项目名称。当然也可以加上多包的定义,-Dpackage=com.company.hellomaven。

接着是maven的配置文件,maven对于工程的配置在pom.xml中。

那么pom.xml的POM是指什么?

POM(Project Object Model)是maven的核心思想。项目对象模型,这个文件中定义了工程的类型、名称、管理依赖关系、定制插件的行为。

当一个大项目被分成几个小项目,每个小项目拥有自己的pom文件,然后有一个共同的父项目。只有构建父项目就能构建所有子项目。

子项目的 POM 会继承父项目的 POM。另外,所有的 POM都继承了一个 Super-POM。Super-POM 设置了一些默认值,比如在上文提到的默认的目录结构,默认的插件等等,它遵循了惯例优于配置的原则。所以尽管我们的这个 POM 很简单,但是这只是你看得见的一部分。运行时候的 POM 要复杂的多。 如果你想看到运行时候的 POM 的全部内容的话,可以运行下面的命令:

1
$mvn help:effective-pom

关于pom.xml文件的编写,这里是一个pom.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.honsen.test</groupId>
<artifactId>first-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>First Maven Project</name>
</project>

第一行指定文档的xml、版本和编码;

第二行即上面说到的每个pom.xml的核心元素——project

其中的几个元素——

1 modelVersion 这个元素指定了POM的版本,Maven2或者Maven3 都只能是4.0.0

2 groupId 是项目组的ID,一般是com.公司组织名.项目名(比如com.honsen.test)

3 artifactId 是该项目在项目组中的ID,比如当前的项目是项目组的一个代理项目,就可以叫做myproxy

4 version 是项目的版本号,用于维护项目的升级和发布

5 name 一般没有实际的用处,只是用于标识该项目

在pom文件中,通过groudId、artifactId、packaging、version等maven坐标来确定一个项目。

另外,一个工程总是需要依赖多个jar包,对于这种jar

包的管理,称之为“依赖管理”。而怎么确定jar包的坐标?maven是通过groupId,artifactId,以及version确定一个唯一的jar包。例如常用的一个Junit的声明是:

1
2
3
4
5
6
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

那么这些jar的包从哪里获取?这里我称之为仓库。Maven中会涉及多种仓库。

1 工作空间,即我们的项目工程,这里面可能会放着pom.xml文件,这个pom.xml就是maven的配置文件

2 本地仓库,本地仓库用于存放jar包,其实Jar包并不是直接放入工作空间的,它是存放在本地仓库,然后在执行发布打包的时候,添加依赖路径

3 私库:私库是使用者自己搭建的maven仓库,用于缓解频繁从外网下载jar包资源的压力。而且使用私库作为缓存层,也相对安全一些。

4 共享仓库:书中所说的中央仓库或者一些常用的镜像网站都属于这种,国内比较著名的oschina以及163都是不错的maven仓库。

当我们在pom.xml中声明了依赖关系后

1 Maven在执行相关的任务时,会先去本地仓库查看是否有该资源,如果有的话,判断版本是否正确,如果一切都没问题则直接使用;否则,执行下一步

2 Maven会去配置的共享仓库中查找,如果找到就拷贝到本地仓库中;找不到则会给出相关的提示

3 Maven在本地如果搭建了私库,则会去私库中查找,找到就拷贝到本地仓库;找不到就会去共享仓库中查找,然后放入私库和本地库。有了私库,局域网内的开发者可以共享依赖,就不用每个人都去外网下载jar包,浪费带宽了。

关于本地仓库和共享仓库的配置都在settings.xml中,这个文件位于conf中,如果没有则拷贝一份即可。

1
<localRepository>F:\apache-maven-3.3.9\repo</localRepository>

共享仓库的地址配置:

1
2
3
4
5
6
7
8
<mirrors>
<mirror>
<id>CN</id>
<name>OSChina Central</name>
<url>http://maven.oschina.net/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>

Maven插件

我们用了 mvn archetype:generate 命令来生成一个项目。那么这里的 archetype:generate 是什么意思呢?archetype 是一个插件的名字,generate是目标(goal)的名字。这个命令的意思是告诉 maven 执行 archetype 插件的 generate 目标。插件目标通常会写成 pluginId:goalId

一个目标是一个工作单元,而插件则是一个或者多个目标的集合。比如说Jar插件,Compiler插件,Surefire插件等。从看名字就能知道,Jar 插件包含建立Jar文件的目标, Compiler 插件包含编译源代码和单元测试代码的目标。Surefire 插件的话,则是运行单元测试。

因为mvn 本身不会做太多的事情,它不知道怎么样编译或者怎么样打包。它把构建的任务交给插件去做。插件定义了常用的构建逻辑,能够被重复利用。这样做的好处是,一旦插件有了更新,那么所有的 maven 用户都能得到更新。

生命周期

上面提到了maven的插件,那么就不得不说maven的生命周期。 上面说到了maven本事不会做太多事情,是指maven的生命周期是抽象的,实际工作由插件完成。

运行的maven的每个步骤由它定义,相比较Ant需要自己去手工定义,一个固定的生命周期模板,预定义的行为让maven变得简单。而同时也一定失去了”灵活性”.

maven有三套相互独立的生命周期(之所以是相互独立,即你可以仅仅调用其中里面的一个):

clean:真正构建之前的清理工作;

default:构建本事的核心内柔,会有编译、测试、打包、部署等内容;

site:生成项目报告,站点,发布站点。

每套生命周期由一组阶段 (Phase)组成,例如

clean-|

|-pre-clean:执行一些需要在clean之前完成的工作

|-clean:移除所有上一次构建生成的文件

|-post-clean:执行一些需要在clean之后立刻完成的工作

mvn clean中的clean就是指clean这一套生命周期中的clean阶段,在一个生命周期中运行某个阶段,这个阶段之前的阶段都会被运行。就是mvn clean实际执行了pre-clean和clean。

同样的,

default-|

|-validate

|-generate-sources

|-process-sources

|-generate-resources

|-process-resources 复制并处理资源文件,至目标目录,准备打包。

|-compile 编译项目的源代码。

|-process-classes

|-generate-test-sources

|-process-test-sources

|-generate-test-resources

|-process-test-resources 复制并处理资源文件,至目标测试目录。

|-test-compile 编译测试源代码。

|-process-test-classes

|-test 使用合适的单元测试框架运行测试,这些测试代码不会被打包或部署。

|-prepare-package

|-package 接受编译好的代码,打包成可发布的格式,如 JAR ,WAR。

|-pre-integration-test

|-integration-test

|-post-integration-test

|-verify

|-install 将包安装至本地仓库,以让其它项目依赖。

|-deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享。

site – |

|-pre-site 执行一些需要在生成站点文档之前完成的工作

|-site 生成项目的站点文档

|-post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备

|-site-deploy 将生成的站点文档部署到特定的服务器上

在是用eclipse时也会有maven的插件来帮助使用。


断断续续在笔记里整理了一段时间了,果然还是要po上来。