主要是翻译一下Better Builds with Maven这本书,尽量精简,做个笔记而已,初学maven有不对的地方请指正。所以也不敢说有什么体会,看一章写一章。
第一章 Maven介绍
Maven是什么
Maven包括一系列构建标准:一个产品库模型,一个管理和描述项目的软件引擎。定义了构建、测试、部署项目产品的标准生命周期。提供了一个简单实用符合Maven标准的通用构建逻辑。是一个在Apache软件基金会下的开源项目,是一个声明式项目管理工具(通过项目对象模型Project Object Model),用来简化软件项目管理过程的框架。
Maven好处
一致(Coherence)
Maven以一系列最佳实践为基础使组织标准化,因为Maven是根据标准化模型构造的。
重用(Reusablity)
Maven构建于可重用的基础之上。当你使用Maven时你可以高效的重用整个行业完整的最佳实践。
敏捷(Agility)
Maven降低了重用构建逻辑和软件组件的门槛。使创建一个组件然后再整合它到多个项目中变得容易。
可维护(Maintainability)
使用Maven的组织不必为了构建而构建,可以集中精力于构造应用程序。Maven项目的可维护性是很高的,因为它遵从了通用的公共定义模型。
Maven原则
习惯优于配置(Convention over configuration)
标准的项目目录结构
这个就不解释了,应该都明白
一个项目一个主输出的思想
举个例子,如果有一个客户端/服务端项目,Maven只能有一个输出,所以Maven鼓励把该项目分割成3个子项目,客户端一个,服务器端一个和公共类库一个,通过依赖性来引用jar包,这样符合依赖拆分(separation of concerns(SoC))原则
标准命名规则
如<artifactId>-<version>.<extension>(产品名-版本号.扩展名)
common-logger-1.2.jar不能叫common-logger.jar,因为缺少版本号。
重用构建逻辑(Reuse of build logic)
Maven鼓励依赖拆分(Soc)。通过封装构建逻辑到一致性的模块中形成插件来执行这一原则。Maven可以被认为是一种协同各种可执行插件的框架。任何东西在Maven中都是插件的运行结果。在Maven中,插件是所有事物的关键构建块。
声明式执行(Declarative execution)
项目对象模型Project Object Model(POM)
Maven是以项目为中心设计的,POM是单个项目的描述。没有POM,Maven毫无用处。POM驱动了Maven的执行,这种方式称为模型驱动或者声明式执行。
POM是一个xml文件如
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
上面的POM可以允许你编译、测试、生成基本的文档,为什么只需要这几行呢,因为Maven有个隐含的Super POM,Super POM是Maven鼓励的一个规则,就像java中的所有类的父类是Object一样,Maven中的所有POM都有一个Super POM。
元素说明:
project:pom.xml的顶层元素
modelVersion:pom文件的版本号,一旦有模块更改就会更新
groupId:说明创建这一项目组织的唯一标识。通常为基于组织的全限定名,例如org.apache.maven.plugins
artifactId:本项目生成产品的标识
packaging:打包类型如(JAR,WAR,EAR...),默认为jar
version:产品的版本号
name:产品显示名
url:项目网站
description:项目描述
所有元素的参考http://maven.apache.org/maven-model/maven.html
构建生命周期(build life cycle)
软件项目的构建路径为:预处理(preparation),编译(compilation),测试(testing),打包(packaging),安装(installation),等。Maven为这种项目提供的这些路径称为构建生命周期。在Maven中构建生命周期由一些列阶段组成,每个阶段可以运行1个或多个与该阶段相关的行为或目标。例如,编译阶段调用一些目标去编译一些类。
在Maven中你所要做的就是告诉Maven你所需要的在标准构建声明周期中的阶段。需要注意的是,每个阶段都会被执行直到你指定的阶段。如,如果你告诉Maven到compile阶段,Maven将自动执行validate,initialize,generate-sources,process-sources,generate-resources,compile这些先于complile的阶段。这些标准构建声明周期由许多阶段组成,这些阶段可以被认为是扩展点。当你想在构建生命周期中加入功能时你可以将可重用的构建逻辑插件直接插入到构建生命周期中,任何时候当你需要优化项目构建时你可以使用一个已存在的插件或者创建一个自定义的插件。
一致性组织和依赖(Coherent organization of dependencies)
看一下Maven是如何做到这点的,下面是个junit的例子
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
dependency表示对产品库(artifact respository)的一个引用,一个dependency通过groupId,artifactId,version来唯一标识一个产品,在Maven中依赖是声明式的,你无需告诉Maven依赖的物理位置,只需告诉项目的特定期望。如上例你无需关心junit jar包的具体位置只需在POM中配置需要什么产品。依赖管理是Maven的一个强大能力。Maven查找所有可访问的远程库(remote respository)中的最接近依赖请求的产品,一旦找到就把它复制到本地库中(local respository)以供项目使用。如果本地库中存在则默认不会查找远程库。
本地库(Local Maven repository)
默认路径:<user_home>/.m2/repository
目录结构:
<组织名,如果有点分割则有多层目录和java包组织方式相同>
<产品>
<版本号>
产品-版本.扩展名
产品-版本.pom
...
定位依赖产品(Locating dependency artifacts)
生成路径 如/groupId/artificatId/version/artificatId-version.jar,先从本地库中查找,如果本地库中不存在则会到远程库中获取。默认情况下Maven会到Maven中心库查找,中心库位置:http://repo1.maven.org/maven2,如有多个远程库,maven会按配置次序获取。一旦依赖满足,产品会被安装到本地库。这样就可以使本机上的所有项目都共享你的本地库,不需要把每个包都复制一份到一个项目下。避免了当项目增多时包容量的不断增大,而且包并不是你的代码的一部分不需要放到版本控制工具中(SCM)。
第二章 起步
2.1 准备使用Maven
下载地址和安装方法:
http://maven.apache.org/download.html
代理配置(如果你在防火墙后面)
创建文件<user_home>/.m2/settings.xml,内容如下
外部代理
<settings>
<proxies>
<proxy>
<active>true</active>
<protocol>http</protocol>
<host>proxy.mycompany.com</host>
<port>8080</port>
<username>your-username</username>
<password>your-password</password>
</proxy>
</proxies>
</settings>
内部代理
<settings>
<mirrors>
<mirror>
<id>maven.mycompany.com</id>
<name>My Company's Maven Proxy</name>
<url>http://maven.mycompany.com/maven2</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
你可以在这里找到settings.xml的详细配置http://maven.apache.org/ref/2.0.8/maven-settings/settings.html
验证安装成功
mvn --version
查看版本号,如果成功你可以进入下一步
2.2 创建第一个maven项目
创建第一个项目需要用到Maven的原型(Archetype)机制。Archetype定义了一个统一的模型,是一个产出完整功能Maven项目的模板。详情请见http://maven.apache.org/guides/introduction/introduction-to-archetypes.html
快速创建Maven项目
mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-app
执行后你会发现在当前目录下创建了my-app文件夹,此文件夹中包括了pom.xml,内容如下
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
你会发现pom.xml在项目的顶层目录下,当你发现一个目录中有pom.xml,你就应该知道你在处理一个Maven项目,该项目按照习惯(Convention)结构生成的目录结构如下
my-app
----src
--------main
------------java
----------------com
--------------------mycompany
------------------------app
----------------------------App.java
--------test
------------java
----------------com
--------------------mycompany
------------------------app
----------------------------AppTest.java
----pom.xml
2.3 编译源程序
进入<my-app>目录,运行命令
mvn compile
下面来分析一下maven执行上述命令的过程,Maven是怎样查找源代码来编译他们,如何知道把它们编译到哪里?这就是Maven的"convention over configuration"原则的作用。默认情况下,应用程序源文件放在src/main/java下,默认值继承于Super POM。这就是说如果你使用默认位置的话,你无需告诉POM你的源代码存放位置,当然在极少数情况下你也可以指定特定的位置,默认的编译类输出路径位于target/classes。
Maven是怎样编译源程序的?这就是Maven的第二个原则"reusable build logic"起得作用。根据默认配置,标准的编译插件会编译你的应用程序源文件。被封装于编译插件中相同的构建逻辑会在任何其他项目中一贯的执行。
Maven是怎样将编译插件和后台进程关联并调用的呢?其实Maven的确有这种形式的映射,叫做默认构建生命周期(default build of life cycle)
Maven是怎样获取插件的呢?在安装完后你找不到编译所需的插件,Maven会从远程库中自动下载所需要的插件。当你第一次执行命令时Maven会从远程下载插件,以后再执行时则不会下载已有插件。
通过使用Maven的习惯配置你可以花很少力气做很多事。
2.4 编译测试源代码和执行单元测试
接下来你要测试你的源程序,这就表示你需要执行在生命周期中所有先于test阶段的步骤,输入如下命令
mvn test
该命令执行动作
下载测试插件
编译源代码,执行测试
如果你仅仅想编译你的测试类可以输入如下命令
mvn test-compile
这个不是必须的因为mvn test总会调用comile,test-compile以及先于test的所有步骤
执行测试类的规则
默认情况下
包括
**/*Test.java
**/Test*.java
**/*TestCase.java
排除
**/Abstract*Test.java
**/Abstract*TestCase.java
2.5 打包和安装到你的本地库
下一个逻辑步骤是打包,命令如下
mvn package
执行完毕后,你会target下看到my-app-1.0-SNAPSHOT.jar包
执行命令安装到本地库供其他项目使用
mvn install
执行完毕后,你会在本地库中默认路径<user_home>/.m2/repository/com/mycompany/app/my-app下看到你的项目和jar包
上述的构建,测试,打包,安装是Maven的主要任务,你也可以调用其他任务。其中一个非常有用的是为你的项目生成网站,命令如下
mvn site
其他命令
在构建之前清理target目录
mvn clean
生成IntelliJ IDEA描述
mvn idea:idea
生成eclipse描述
mvn eclipse:eclipse
2.6 处理classpath资源
另一个频繁使用的情况是打包资源文件到JAR文件中。对于这种任务,Maven再一次使用标准目录结构。意味着只要采用Maven的标准习惯,你就可以将资源打包到JARs,而你所要作的仅仅是把资源放到标准目录结构中。
Maven的规则是src/main/resources目录中的所有资源都会以相同的名字打包到JAR的根目录下
练习:
在你的src/main/resources下新建META-INF/application.properties然后运行mvn install,解压目的输出jar包就能发现META-INF下包括了这个文件,并且META-INF下还包括了maven/com.mycompany.app/my-app/{pom.properties,pom.xml},这使得Maven生成的jar包有自描述的能力,其他项目可能会需要该产品的信息例如版本号,通过POM(pom.xml)获取,这需要Maven支持,或者通过属性文件(application.properties)获取,这个只需java APIs。
2.6.1 处理测试路径下的classpath资源
要在单元测试中加入资源,你需要把资源放到src/test/resources目录下
当你的测试程序中需要访问资源时的示例代码片段如下
[...]
// Retrieve resource test.properties是放到src/test/resources下的资源
InputStream is = getClass().getResourceAsStream( "/test.properties" );
// Do something with the resource
[...]
如果需要覆盖你自己的manifest文件,你可以使用如下配置maven-jarplugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
2.6.2 过滤classpath资源
有时可能需要根据属性过滤资源——动态产生所需要的资源中的某些值,要达到这个目的maven中使用${<property name}标记,一个属性可以是定义在pom.xml(项目级别)中,定义在settings.xml(本机级别)中或者外部属性文件(properties文件)或者系统属性(java -D后的属性或者java自带的runtime属性),要使用过滤必须设置build元素中resources下的resource中的filtering为true,配置文件如下
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
引用pom.xml里的属性
只要引用标签名就可以了如${poject.name}引用了pom的project的项目显示名,${project.version}引用了版本名,${project.build.finalName}引用了最终打包生成的文件名
练习:
创建文件src/main/resources/META-INF/
application.properties
输入
# application.properties
application.name=${project.name}
application.version=${project.version}
application.build.finalName=${project.build.finalName}
执行mvn mvn process-resources,该命令指运行资源复制和筛选的构建生命周期
执行完毕后target/classes/application.properties这个文件中的值变为
# application.properties
application.name=my-app
application.version=1.0-SNAPSHOT
application.build.finalName=my-app-1.0-SNAPSHOT
引用外部文件的属性
练习:
创建src/main/filters/filter.properties
把外部属性文件的引用加入pom文件中
<build>
<filters>
<filter>src/main/filters/filter.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
加入属性到刚才的src/main/resources/META-INF/
application.properties文件中
# application.properties
application.name=${project.name}
application.version=${project.version}
application.build.finalName=${project.build.finalName}
message=${my.filter.value}
执行mvn mvn process-resources,结果如下
# application.properties
application.name=my-app
application.version=1.0-SNAPSHOT
application.build.finalName=my-app-1.0-SNAPSHOT
message=hello!
也可以把属性直接定义在pom中,效果相同,如下最后properties元素
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
< filtering>true</filtering>
</resource>
</resources>
</build>
<properties>
<my.filter.value>hello</my.filter.value>
</properties>
</project>
引用系统属性
如mvn process-resources "-Dcommand.line.prop=hello again"
配置方式和上面例子类似
2.6.3 防止过滤二进制文件
当需要防止过滤某些文件时,你需要定义一个防止过滤的入口和一个资源入口
例如你要防止过滤src/main/resources/images下的文件,配置如下
<project>
[...]
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>images/**</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>images/**</include>
</includes>
</resource>
</resources>
</build>
[...]
</project>
2.7 使用maven插件
如前所述如需要自定义构建Maven项目,你需要包括额外的插件或者配置已存在的插件参数。
例如,你可能像配置java编译器jdk的版本,配置文件如下
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
你可能已经注意到plugin的配置和dependency的配置很相似。如果该插件没有出现在你的本地系统中他会以dependency同样的方式去远程库下载并自动安装。为了说明两者的相似性,上述配置显示了groupId和version元素,但是大多数情况下这不是必须的。
默认情况下如果你没有定义groupId,Maven将会以org.apache.maven.plugins或org.codehaus.mojo为groupId去寻找,你也可以定义了一个额外的groupId在POM或setting.xml中。
如果你没有定义版本,Maven会假设下载最新的插件版本。虽然插件一般向下兼容,但是为了兼容性,你也可以定义版本号。
如果你想查看插件的属性选项,可以使用命令mvn help:describe
例如:你想查看maven-compiler-plunin的选项输入如下命令
mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-compiler-plugin -Dfull=true
也可以通过插件参考手册查看详情网址:http://maven.apache.org/plugins/
一篇中显示不下故分成多篇,下一篇:http://hrtc.iteye.com/admin/blogs/181421