Maven 入门
一、认识 Maven
Maven /ˈmāvən/ ,可以翻译成“专家”,是一款来自 Apache 组织的开源项目,用于项目管理。主要服务于基于 Java 平台的项目构建、依赖管理和项目信息管理。
构建(build)是每一位程序员每天都在做的事情,每天都有相当一部分时间花在编译、运行单元测试、生成文档、打包和部署等工作上,这些就是构建。而这一切,Maven 都可以帮我们自动完成,提高工作效率。
1、Ant vs Maven
Ant 只能算作一个构建工具,而 Maven 是一个项目管理工具,更正式的定义为:Maven 是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期 (Project Lifecycle),一个依赖管理系统 (Dependency Management System),和用来运行定义在生命周期阶段 (phase) 中插件 (plugin) 目标 (goal) 的逻辑。(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 Ant
1,Ant 没有正式的约定如一个一般项目的目录结构,你必须明确的告诉 Ant 哪里去找源代码,哪里放置输出。随着时间的推移,非正式的约定出现了,但是它们还没有在产品中模式化。
2,Ant 是程序化的,你必须明确的告诉 Ant 做什么,什么时候做。你必须告诉它去编译,然后复制,然后压缩。
3,Ant 没有生命周期,你必须定义目标和目标之间的依赖。你必须手工为每个目标附上一个任务序列。
<?xml version="1.0"?>
<project name="Hello" default="compile">
<target name="compile" description="compile the Java source code to class files">
<mkdir dir="classes"/>
<javac srcdir="." destdir="classes"/>
</target>
<target name="jar" depends="compile" description="create a Jar file">
<jar dstfile="hello.jar">
<fileset dir="classes" includes=" **/ *.class"/>*
<mainfest>
<attribute name="Main-Class" value="HelloProgram"/>
</mainfest>
</jar>
</target>
</project>
Apache Maven
1,Maven 拥有约定,因为你遵循了约定,它已经知道你的源代码在哪里。它把字节码放到 target/classes ,然后在 target 生成一个 JAR 文件。
2,Maven 是声明式的。你需要做的只是创建一个 pom.xml 文件然后将源代码放到默认的目录。Maven 会帮你处理其它的事情。
3,Maven 有一个生命周期,当你运行 mvn install 的时候被调用。这条命令告诉 Maven 执行一系列的有序的步骤,直到到达你指定的生命周期。遍历生命周期旅途中的一个影响就是,Maven 运行了许多默认的插件目标,这些目标完成了像编译和创建一个 JAR 文件这样的工作。
2、被误解的 Maven
C++ 之父 Bjarne Stroustrup 说过一句话:“只有两类计算机语言,一类语言天天被人骂,还有一类没人用”。
用户最多的 Java 得到的骂声就不绝于耳,Maven 的用户也提出了很多质疑:
“Maven 对于 IDE 的支持较差,bug 多,而且不稳定”
Maven 最高效的方式永远是命令行,IDE 在自动化构建方面有天生的缺陷。
“Maven 采用了一种糟糕的插件系统来执行构建,新的、破损的插件会让你的构建莫名其妙地失败”
自 Maven 2.0.9 开始,所有核心的插件都设定了稳定版本,Maven 社区也提倡为你使用的任何插件设定稳定的版本,从 Maven 3 开始,如果你使用插件时未设定版本,会看到警告信息。
“Maven 的仓库十分混乱,当无法从仓库中得到需要的类库时,我需要手动下载复制到本地仓库中”
Maven 的中央仓库确实不完美,你也许会发现某个 jar 包出现在两个不同的路径下,而这是开源项目本身改变了自身的坐标,假设一下没有中央仓库,你需要从开源项目首页寻找下载链接,这个反而是更痛苦的事情。
“缺乏文档是理解和使用 Maven 的一个主要障碍”
这是事实, Maven 官方站点的文档十分凌乱,各种插件的文档更是需要费力寻找。
二、Maven 的安装和配置
1、安装
Windows 环境
- Maven 官网下载安装文件
- 解压到指定目录
- 配置环境变量(M2_HOME)
- cmd 输入 “mvn -v”
Mac环境
“brew install maven”
- 配置环境变量
- export M2_HOME=/usr/local/Cellar/maven/3.5.4,
- export PATH=$PATH:$M2_HOME/bin
- 终端输入:“mvn -v”
2、配置
- Eclipse:m2eclipse
- IDEA:自带+辅助插件
3、最佳实践
1、设置 MAVEN_OPTS 环境变量
运行 mvn 命令实际上是执行了 Java 命令,那么 Java 命令可用的参数同样可用在运行 mvn 命令时可用。
通常需要设置 MAVEN_OPTS 的值为 -Xms128m -Xmx512m(堆内存的初始值和最大值),因为 Java 默认的最大可用内存往往不够满足 Maven 运行的需要,比如在项目较大时,使用 Maven 生成项目站点需要占用大量的内存,如果没有该配置,很容易得到 java.lang.OutOfMemeoryError
,因此,最好提前配置该变量。
设置方式建议参考 M2_HOME 变量的配置方式,不要直接更改安装目录下的文件,不然版本更新以后还要重新配置该变量。
2、配置用户范围 settings.xml
Maven 用户可以选择配置 $M2\_HOME/conf/settings.xml
或者 ~/.m2/settings.xml
,前者是全局范围的,后者是用户范围的,推荐使用用户范围的 settings.xml,主要是为了避免影响其他的用户,而且配置用户范围的 settings.xml 文件还便于 Maven 升级,升级时不会影响到 Maven 的安装文件,也不会影响到使用。因为使用有个加载顺序的,先加载用户的配置文件,没有匹配再加载系统的配置文件:
3、不用使用 IDE 内嵌的 Maven
无论 Eclipse 还是 IDEA,当集成 Maven 时,都会安装上一个内嵌的 Maven,这个内嵌的 Maven 通常会比较新,但是不一定稳定,而且往往也会和在命令行使用的 Maven 不是同一个版本。这样就有可能因为版本不同的原因出现某些问题,所以建议还是用本地安装的 Maven 版本,而本地安装的版本也应该与服务器上安装的版本一致。
三、认识 Maven 结构
1、settings.xml 元素解读
localRepository
该元素表示本地 Maven 仓库的地址,不设置的话,默认为 ~/.m2/repository
pluginGroups
将插件的信息注册到 Maven 中,是的执行 Maven plugin 命令的时候可以不指定 groupId 和 artifactId,比如:
这个生成源代码的插件,运行的时候不需要指定它的 groupId 和 artifactId,只需要执行mybatis-generator:generate
即可,因为这个插件的信息属于默认的两个插件组 org.apache.maven.plugins
和org.codehaus.mojo
其中的一个,如果是其他的话,则需要显式地配置一下。
servers
配置的私服的登录信息,比如 username
、password
等服务器的认证信息,也可以设置权限信息。
mirrors
远端的中央仓库,有时候下载第三方的 jar 包比较慢,可以更改为国内的一些镜像仓库,比如阿里云的仓库:
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
如果是公司内部使用的 jar 包,可以放在自己搭建的私服上面,这里配置成自己的私服地址即可。
profiles、activeProfiles
其作用主要用于区分环境用的,也可以定义一些仓库,用来搜索需要的发布版或者快照版来构建,这个配置中的 profile,如果在激活列表里边存在,则这些 profile 将会覆盖 pom.xml 中的相同 id 的 profile。
2、pom.xml 元素解读
modelVersion
如果使用的是 Maven 3.0 及以上的版本,这里的值默认都是 4.0.0,而这个值来自哪里呢,在 Maven 安装目录里 lib 目录里边的 /lib/maven-model-builder-3.5.4.jar
,解压之后,进入到目录 org/apache/maven/model
里边,会看到一个 pom.xml 文件:
而且,继续往下看,你就会发现,为什么用 IDEA 生成的 Maven 项目,它的目录结构是约定好的,约定的配置就是这个 super pom,这样只要是使用 Maven 开发的项目,其目录结果都是一样的,这种思想就是常常听说的Convention Over Configuration(约定优于配置)。
groupId
定义当前 Maven 项目隶属的实际项目,由于经常会有多模块的 Maven 项目,所以 Maven 项目与实际项目不一定是一对一的关系,因此,groupId
不应该对应项目隶属的组织或公司,应该到具体的项目;实际的表示方式也应该与 Java 包名的表示方式类似,通常与域名反向一一对应。
artifactId
该元素定义实际项目中的一个 Maven 项目(模块),推荐的做法是使用实际项目名称作为 artifactId 的前缀,这样做的好处是方便寻找实际构件。
version
该元素定义 Maven 项目当前所处的版本。
packaging
打包方式,默认为 jar。
properties
用于定义一些配置常量,比如依赖的版本号。
dependencyManagement
该元素只能出现在父 pom.xml 中,其作用是为了统一版本号,而且这里的依赖只是一个声明,子 pom.xml 里用到其中某一个的时候,再去显式地引用。
dependency
引用依赖,其中的配置有如下几个:
- type,默认为 jar
- scope 表示其作用范围,有如下几个范围:
- compile,编译时依赖,也是默认的依赖范围,编译、测试和运行都需要,比如 spring-core
- test,测试时依赖,只在测试阶段需要,比如 spring-test
- provided,编译时依赖,只在编译时需要,比如servlet
- runtime,运行时依赖,只在运行时需要,比如JDBC驱动类
- system,本地的一些 jar,比如短信的 jar 包,常用 systemPath一起使用
- exclusions,用来 排除由于传递依赖引入(参考第三小节)的但是是不需要的依赖
- optional,可选依赖,默认为 false,用于放置依赖传递,当一个项目 A 依赖另一个项目 B 时,项目 A 可能很少一部分功能用到了项目 B,此时就可以在 A 中配置对 B 的可选依赖y
3、传递性依赖
传递性依赖可以减少一些引用的依赖,可以进行隐式地依赖,但是如果需要控制版本,最好的方式是,先排除该依赖,再显式地引用该依赖,依赖关系如下图:
4、依赖仲裁
如果根据传递性依赖,同时依赖了两次某一个 jar 包,例如,项目 A 有这样的依赖关系:A -> B -> C -> X(1.0)、A -> D -> X(2.0),X 是 A 的传递性依赖,那么哪个 X 会被 Maven 解析使用呢,Maven 依赖仲裁(Maven Mediation)的第一原则是:路径最近者优先,该例中 X(1.0) 的路径长度为 3,而 X(2.0) 的路径长度为 2,因此 X(2.0) 会被解析使用。
再比如这样的依赖关系:A -> B -> Y(1.0),A -> C -> Y(2.0),Y(1.0) 和 Y(2.0) 的依赖路径长度是一样的,都为 2,根据 Maven 依赖仲裁(Maven Mediation)的第二原则:第一声明者优先,在 POM 中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优先被解析,所以 Y(2.0) 就会被解析使用。
5、优化依赖
mvn dependency:analyze
使用但未声明的依赖(Used undeclared dependencies),建议显示声明
声明但未使用的依赖(Unused declared dependencies),有可能是运行时使用的