导语
最近在学习Java了,以后分享的文章主要就以Java为主了,偶尔也会分享一下Objective-C方面的文章,这篇读书笔记主要介绍了Maven的一些核心概念和常用的一些插件。
Maven
什么是Maven?
什么是Maven呢?我们看下官网给出的一段介绍:
Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
从介绍中我们可以看到Apache Maven是一个项目管理和理解工具,它基于项目对象模型(POM)的概念,它可以管理项目的构建、报告和文档。我们也会经常听到有人对Maven是这样理解的:
- Maven是一个站点和文档工具。
- Maven扩展Ant,让你下载依赖关系。
- Maven是一组可重用的Ant脚本。
这几个方面都是Maven的一些功能,Maven所提供的功能远比这强大的许多,不过对于我们在日常项目当中用到最多的就是管理第三方库的依赖、项目的构建。如果开发工具使用的是IntelliJ IDEA,自动就帮你安装好了Maven,不是的话,也可以参考下官方给出的Maven安装教程,也是比较简单的。接下来看下怎样用Maven来满足我们项目的日常需要。
约定优于配置
Maven使用约定优于配置的原则,如下所示:
目录 | 目的 |
---|---|
${basedir} | 存放pom.xml和所有的子目录 |
${basedir}/src/main/java | 项目的java源代码 |
${basedir}/src/main/resources | 项目的资源,比如说property文件,springmvc.xml |
${basedir}/src/test/java | 项目的测试类,比如说Junit代码 |
${basedir}/src/test/resources | 测试用用的资源 |
${basedir}/src/main/webapp/WEB-INF | web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面 |
${basedir}/target | 打包输出目录 |
${basedir}/target/classes | 编译输出目录 |
${basedir}/target/test-classes | 测试编译输出目录 |
Test.java | Maven只会自动运行符合该命名规则的测试类 |
~/.m2/repository | Maven默认的本地仓库目录位置 |
一个maven项目在默认情况下会产生jar文件,另外,编译后的classes会放在${basedir}/target/classes下面,jar文件会放在${basedir}/target下面。使用约定优于配置带来最大的好处就是项目的统一,任何人在使用Maven项目的时候,文件的存放位置都是一样的,通用性比较好。这也是为什么我们在用intellij创建一个maven项目的时候,需要配置源文件、资源文件路径的原因。如下图就是常用的几个配置:
Maven的几个核心概念
POM(Project Object Model)
一个项目所有的配置都放在POM文件中:定义项目的类型、名字、管理依赖关系,定制插件的行为等等。看下我自己写的小demo中pom中配置如下:
com.dodonew
springmvc
1.0-SNAPSHOT
war
springmvc
http://maven.apache.org
org.slf4j
jcl-over-slf4j
1.7.21
ch.qos.logback
logback-classic
1.1.7
在POM中,groupId、artifactId、packaging、version叫做maven坐标,它能唯一的确定一个项目。有了maven坐标,我们就可以用它来指定我们的项目所依赖的其他项目、插件、或者父项目。我写的demo很简单,但是比较大的项目一般会分成几个子项目,在这种情况下,每个子项目就会有自己的POM文件,它们会有一个共同的父项目。这样只要构建父项目就能够构建所有的子项目了。同时子项目的POM会继承父项目的POM。关于多个子项目具体怎么操作,在文章后面会有回答的。
Maven插件
Maven常用的插件比如compiler插件、surefire插件、jar插件。比如说jar插件包含建立jar文件的目标,compiler插件包含编译源代码和单元测试代码的目标,surefire插件则是运行单元测试的目标。为什么需要这些插件呢?因为maven本身不会做太多的事情,它不知道怎么样编译或者怎么样打包。它把构建的任务交给插件去做。插件定义了常用的构建逻辑,能够被重复利用。这样做的好处是,一旦插件有了更新,那么所有maven用户都能得到更新。
Maven生命周期
生命周期指项目的构建过程,它包含了一系列的有序的阶段,而一个阶段就是构建过程中的一个步骤,比如package阶段、compiler阶段等。那么生命周期阶段和上面说的插件目标之间是什么关系呢?插件目标可以绑定到生命周期阶段上,一个生命周期可以绑定多个插件目标。当maven在构建过程中逐步的通过每个阶段时,会执行该阶段所有的插件目标。目前生命周期阶段有clean、vavidate、compiler、test、package、verify、install、site、deploy阶段。
Maven依赖管理
我们能够通过maven坐标确定一个项目,换句话说,我们可以用它来解决依赖关系。在POM中,依赖关系是在dependencies部分中定义的。比如对slf4j库和logback库的依赖关系如下:
org.slf4j
jcl-over-slf4j
1.7.21
ch.qos.logback
logback-classic
1.1.7
这个依赖关系是比较简单的,但是实际开发中我们会有复杂多的依赖关系,因为被依赖的jar文件会有自己的依赖关系,那么我们是不是也得把间接依赖的jar文件也都定义在POM中呢?答案是不需要,因为maven提供了传递依赖的特性,会把依赖的jar文件它所依赖的库也自动添加进来。比如spring-webmvc库,这个库自己又依赖spring-core、spring-beans、spring-web、spring-expression、spring-context,因为依赖传递性,我们在使用spring-webmvc的时候,只需要声明对spring-webmvc的依赖关系即可,其他的库会自动帮你引入的。之所以能实现依赖传递性关键就在于下载spring-webmvc库,同时也下载了pom文件,在pom文件定义了这个库需要的依赖关系。
scope决定了依赖关系的适用范围,比如junit的scope是test,那么它只会在执行compiler:testCompile和surefire:test目标的时候才会被加载到classpath中,在执行compiler:compile目标时是拿不到junit的。换句话说就是在maven哪个生命周期中起作用了。scope的默认值是compile,即任何时候都会被包含在classpath中,在打包的时候也会被包括进去。
Maven库
我们所依赖的库是从maven默认的远程库 (http://repo.maven.org/maven2) 下载的,这个是公有的库。有时公司自己封装了一些私有库,这个时候我们就可以搭建自己的私有库了。本地库是指maven下载了插件或者jar文件后存放在本地机器上的拷贝。在Mac上,它的位置在~/.m2/repository。当maven查找需要的jar文件时,它会先在本地库中查找,只有在找不到的情况下,才会去远程库中找的。由于maven默认的远程库服务器在国外,国内访问的时候比较慢,建议替换成阿里云的镜像,仓库地址如下:
http://maven.aliyun.com/nexus/content/groups/public/
Maven多模块项目POM注意的事项
denpendencyManagement
在项目开发过程中,有时一个项目下面包含了几个子模块,在多模块的情况,POM的配置应该要注意写什么呢?我们通过一个例子来说明下。
有这样一个工程,里面有A模块、B模块和C模块,A模块需要引入junit和log4j库,配置如下:
junit
junit
3.8.2
log4j
log4j
1.2.9
此时B模块也需要引入这两个库,配置如下:
junit
junit
4.8.2
test
log4j
log4j
1.2.16
会发现A模块和B模块对junit和log4j库依赖的版本是不同的,出现这种情况是十分危险的,因为依赖不同版本的库可能会造成很多未知的风险。怎么解决不同模块之间对同一个库的依赖版本一样呢?Maven提供了优雅的解决办法,使用继承机制以及dependencyManagement元素来解决这个问题。如果你在父模块中配置dependencies,那么所有的子模块都自动继承,不仅达到了依赖一致的目的,还省了大段的代码,但这样来做会存在问题的。比如B模块需要spring-aop模块,但是C模块不需要spring-aop模块,如果用dependencies在父类中统一配置,C模块中也会包含有spring-aop模块,不符合我们的要求。但是用dependencyManagement就没有这样的问题。dependencyManagement只会影响现有依赖的配置,但不会引入依赖。这样我们在父模块中的配置可以更改如下所示:
junit
junit
4.12
test
log4j
log4j
1.2.17
这段配置不会给任何子模块引入依赖,如果某个子模块需要junit和log4j,只需要这样配置即可:
junit
junit
log4j
log4j
在多模块Maven项目中,使用dependencyManagement能够有效地帮我们维护依赖一致性。
pluginManagement
上面介绍了在多模块中对依赖库的管理,接下来介绍下对插件的管理。与dependencyManagement类似,我们可以使用pluginManagement元素管理插件。一个常见的用法就是我们希望项目所有模块的使用compiler插件的时候,都是用java1.7,以及指定Java源文件编码为UTF-8,这时可以在父模块的POM中如下配置pluginManagement:
org.apache.maven.plugins
maven-compiler-plugin
2.3.2
1.5
UTF-8
这段配置会被应用到所有子模块的compiler插件中,因为Maven内置了与compiler插件与生命周期的绑定,因此子模块不需要任何maven-compiler-plugin的配置了。
Maven常用的几个插件
Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成。下面说几个常用的插件:
maven-compiler-plugin(编译插件)
用来编译Java代码,在对Java代码进行编译的时候,可以指定使用哪个JDK的版本来进行编译,配置如下所示:
org.apache.maven.plugins
maven-compiler-plugin
3.6.0
1.7
maven-resources-plugin(资源插件)
Maven区别对待Java代码和资源文件,maven-resources-plugin则用来处理资源文件。默认的主资源文件目录是src/main/resources,很多时候会需要添加额外的资源文件目录,这个时候就可以通过配置maven-resources-plugin来实现,配置如下所示:
org.apache.maven.plugins
maven-resources-plugin
2.5
compile
maven-surefire-plugin(测试插件)
Maven2/3中用于执行测试的插件不是maven-test-plugin,而是maven-surefire-plugin,其实在大部分情况下,只要你的测试类遵循通用的命令约定(以Test结尾,以TestCase结尾、或者Test开头),就几乎不用知晓该插件的存在。但是当你想要跳过测试、排除某些测试类、或者使用一些TestNG特性的时候,就要用到了maven-surefire-plugin的一些配置选项了,配置如下所示:
org.apache.maven.plugins
maven-surefire-plugin
2.14
true
maven-clean-plugin(清除插件)
主要作用就是清理构建目录下的全部内容,有些项目,构建时需要清理构建目录以外的文件,比如指定的库文件,这时候就需要配置
org.apache.maven.plugins
maven-clean-plugin
3.0.0
false
${basedir}/logs
false
maven-war-plugin(打包插件)
主要作用就是用来打包的,在打包的时候经常需要排除一些文件,就需要对warSourceExcludes进行配置了,配置如下所示:
maven-war-plugin
2.0.2
WEB-INF/lib/**
总结
Maven是一个项目管理和自动化构建工具,项目遵循约定优于配置,这也是maven项目的一大特色。另外,maven本质上是一个插件框架,它的核心不执行任何具体的构建工作,全部都交给插件去执行,maven插件是与maven生命周期绑定在一起的。理解这些重要的核心点,对于maven的使用会有很大的帮助。
参考文章
Maven入门介绍
http://www.oracle.com/technetwork/cn/community/java/apache-maven-getting-started-1-406235-zhs.html
http://www.oracle.com/technetwork/cn/community/java/apache-maven-getting-started-2-405568-zhs.html
https://maven.apache.org/what-is-maven.html
Maven常用插件
http://www.infoq.com/cn/news/2011/04/xxb-maven-7-plugin
http://www.infoq.com/cn/news/2011/05/xxb-maven-8-plugin