目录
概述
如今我们构建一个项目需要用到很多第三方的类库,如写一个使用Spring的Web项目就需要引入大量的jar包。
一个项目Jar包的数量之多往往让我们瞠目结舌,并且Jar包之间的关系错综复杂,一个Jar包往往又会引用其他Jar包,缺少任何一个Jar包都会导致项目编译失败。
以往开发项目时,程序员往往需要花较多的精力在引用Jar包搭建项目环境上,而这一项工作尤为艰难,少一个Jar包、多一个Jar包往往会报一些让人摸不着头脑的异常。
而Maven就是一款帮助程序员构建项目的工具,我们只需要告诉Maven需要哪些Jar 包,它会帮助我们下载所有的Jar,极大提升开发效率。
maven目录结构
作为maven项目必须要实现的目录结构
maven的安装和配置
eclipse4.3安装maven
Idea与maven整合
maven命令
maven的常用命令
- 创建Maven的普通java项目:
mvn archetype:create -DgroupId=packageName -DartifactId=projectName - 创建Maven的Web项目:
mvn archetype:create -DgroupId=packageName -DartifactId=webappName-DarchetypeArtifactId=maven-archetype-webapp - 编译源代码: mvn compile
- 编译测试代码:mvn test-compile
- 运行测试:mvn test
- 产生site:mvn site
- 打包:mvn package
- 在本地Repository中安装jar:mvn install
- 清除产生的项目:mvn clean
- 生成eclipse项目:mvn eclipse:eclipse
- 生成idea项目:mvn idea:idea
- 组合使用goal命令,如只打包不测试:mvn -Dtest package
- 编译测试的内容:mvn test-compile
- 只打jar包: mvn jar:jar
- 只测试而不编译,也不测试编译:mvn test -skipping compile -skipping test-compile
( -skipping 的灵活运用,当然也可以用于其他组合命令) - 清除eclipse的一些系统设置:mvn eclipse:clean
maven的坐标
无论自己建立的maven项目还是第三方的maven依赖包都有坐标,想要查找一个依赖包就根据坐标来找。
- groupId: 组织名称+"."+项目名称
- artifactId:项目的模块名
- version:项目版本号
示例如下
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
maven的依赖
依赖管理简述
在pom.xml中配置
<dependencies>
<dependency>
//要依赖的包的坐标
</dependency>
</dependencies>
对第三方依赖包的查找去http://mvnrepository.com/来查找第三方包的坐标即可
依赖管理还可以对自己的项目做依赖
- 不需要把被依赖的工程安装到仓库
- 被依赖工程的classpath下的代码就相当于在依赖工程classpath下
依赖范围
在pom.xml文件中,有个元素是scope,用来表示依赖的范围。之所以会有依赖范围,是因为Maven在编译、测试和运行项目时会各自使用一套classpath,依赖范围就是用来控制这三种classpath的。
- Java主目录的classpath
- test测试目录的classpath
- 运行时的classpath
简单来说,就是通过scope元素来控制项目的依赖是在编译时导入,还是在测试或运行项目时才导入。
使用<scope></scope>来配置依赖的范围,常用的依赖范围:compile,test,provided,runtime
<scope>compile</scope>
三套classpath需要这个包<scope>test</scope>
只对test的classpath有效<scope>provided</scope>
只对test和Java主目录的classpath有效<scope>runtime</scope>
只对运行时的classpath有效
依赖传递
有时候我们在pom.xml文件中引入的依赖,其本身就需要依赖于其他的依赖,这时候我们不需要去考虑这些依赖,Maven会解析各个直接依赖的pom,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
通过传递性依赖,我们可以在pom.xml文件中少写不少的依赖配置。
例如:
A<---B第一直接依赖
B<---C第二直接依赖
A<---C间接依赖
这样A和C之间就有了间接依赖,这就叫依赖的传递性,但是这种依赖传递也不是什么情况下都会无保留地传递,也会根据依赖范围改变而改变。
表格的第一列是B在A中的依赖范围,第一行是C在B中的依赖范围,交叉的格子是C在A中的依赖范围。
总的来说
- 当C在B中的scope为test时,A不依赖C,C直接被丢弃
- 当C在B中的scope为provided时,只有当B在A中的scope也是provided时,A才- 会依赖C,这时候C在A的scope是provided
- 当C在B中的scope为compile或runtime时,A依赖C,此时C在A中的scope继承自B在A的scope。注意,如果C的scope是runtime,B的scope是compile,此时C在A的scope是runtime,而不是compile
需要注意的是A对log4j1.2.9版本依赖,B对log4j1.2.8版本依赖,那么C就会依赖log4j1.2.8版本,当A和B存在依赖时,而且A和B中都依赖了一个依赖包,但是版本不一样,C就会依赖步长较近的版本。
可选依赖
如果我们不想要依赖传递下去,那么也可以配置
<dependency>
<groupId>cn.itcast.maven</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
<optional>false</optional>
</dependency>
当optional是true这个依赖包就不会传递下去,如果是false就可以传递,默认是false。
排除依赖
如果想要主动排除传递下来的依赖,那么可以通过exclusions配置要排除的依赖包的groupId和artifactId即可。
<dependency>
<groupId>cn.itcast.maven</groupId>
<artifactId>HelloFriend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>cn.itcast.maven</groupId>
<artifactId>hello</artifactId>
</exclusion>
</exclusions>
</dependency>
maven仓库
maven仓库是用来存储依赖包和插件的位置的文件夹,总共可以分为3种仓库,分别是中央仓库,本地仓库和私服仓库。
中央仓库
是互联网上一个巨大maven的仓库,这里面有绝大多数的依赖包,但是也有少数的依赖包不在中央仓库中,在这个http://mvnrepository.com/
网站中可以找到中央仓库中的依赖包的坐标,我们不能上传依赖包。
本地仓库
默认情况下在~/.m2/repository/
文件夹就是我们的仓库,当我们来执行maven的命令或者添加依赖包时,如果本地仓库不存在我们需要的依赖,那么maven就会到中央仓库去下载到本地仓库。
如果第二次再来使用同样的依赖包就不需要下载了,本地仓库可以上传或者安装自己的依赖包,使用的命令是mvn install。
本地苍库的位置可以修改,我们最好不要把它放在默认位置,修改时直接把仓库做剪切放到指定位置,修改用户范围的settings.xml,把localRepository的标签值修改成改变后的仓库的位置,然后rebuild index。
私服仓库
是一种特殊的远程仓库,它是架设在本地局域网内的仓库,当团队成员进行开发时,每个人都要从中央仓库下载项目构建到本地仓库,费时费力,这个时候可以在局域网环境搭建一个私人仓库供团队使用,这就是maven私服仓库。
私服仓库具体安装过程如下:
Maven仓库管理
maven的生命周期
Maven的生命周期分为互相独立的三部分,分别是clean,default,site,而每一个周期又包括若干阶段。
clean:用于清理项目,分为3个阶段
default:用于构建项目,分为23个阶段
site:用于建立项目站点,分为4个阶段
阶段
周期所包含的阶段是有前后顺序的,并且后面的阶段依赖于前面的阶段。
阶段的使用
在执行maven命令时,mvn命令之后使用的参数便是maven的各个阶段。
例如:
mvn compile
此命令使用了deault周期的compile阶段。
例如:
mvn clean deploy
此命令使用了clean周期的clean阶段和default周期的deploy阶段。
阶段的顺序
在调用maven的阶段时,会顺序执行本周期当前阶段之前的所有阶段,直至当前阶段。
例如:clean周期共有3个阶段,按顺序分别如下:
1. pre-clean
2. clean
3. post-clean
当执行命令
mvn compile
compile阶段属于default周期,而default周期有23个阶段,compile排序第7位,所以实际执行了default周期的第1阶段validate至第7阶段compile,共7个阶段。包括:
1. validate
2. initialize
3. generate-sources
4. process-sources
5. generate-resources
6. process-resources
7. compile
Maven内部是通过插件中的目标来实现各个阶段的执行
插件(plugin)和目标(goal)
Maven的操作是基于不同的插件的不同目标来实现的
一个插件中包括若干目标,而每个目标会执行一个特定的任务task。
例如:
mvn clean
在上面这个命令中,使用的参数是clean阶段,而实际执行的是maven-clean-plugin插件的clean目标。
因为clean周期的clean阶段是默认绑定到clean插件的clean目标绑定的。(不同的clean)
可在maven-core-*.jar包的META-INF/plexus/components.xml中找到此绑定关系。
<component>
<role>org.apache.maven.lifecycle.Lifecycle</role>
<implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
<role-hint>clean</role-hint>
<configuration>
<id>clean</id>
<phases>
<phase>pre-clean</phase>
<phase>clean</phase>
<phase>post-clean</phase>
</phases>
<default-phases>
<clean>
org.apache.maven.plugins:maven-clean-plugin:2.5:clean
</clean>
</default-phases>
</configuration>
</component>
其它详细的绑定关系可查看META-INF/plexus目录下的xml文件。
生命周期与阶段的关系表及部分阶段默认绑定的插件目标
顺序 | 生命周期 | 阶段 | 插件:目标 |
---|---|---|---|
1 | clean | pre-clean | |
2 | clean | clean:clean | |
3 | post-clean | ||
———— | ———— | ——————————— | ——————————— |
1 | default | validate | |
2 | initialize | ||
3 | generate-sources | ||
4 | process-sources | ||
5 | generate-resources | ||
6 | process-resources | resources:resources | |
7 | compile | compiler:compile | |
8 | process-classes | ||
9 | generate-test-sources | ||
10 | process-test-sources | ||
11 | generate-test-resources | ||
12 | process-test-resources | resources:testResources | |
13 | test-compile | compiler:testCompile | |
14 | process-test-classes | ||
15 | test | surefire:test | |
16 | prepare-package | ||
17 | package | ||
18 | pre-integration-test | ||
19 | integration-test | ||
20 | post-integration-test | ||
21 | verify | ||
22 | install | install:install | |
23 | deploy | deploy:deploy | |
———— | ———— | ——————————— | ——————————— |
1 | site | pre-site | |
2 | site | site:site | |
3 | post-site | ||
4 | site-deploy | site:deploy |
在生命周期里加入自己想要添加的插件
可以直接在maven项目的pom文件的插件里添加,主要是配置插件需要在生命周期的哪一个阶段的哪一个目标执行。
<plugin>
<!--
要使用的插件坐标
-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<!-- 要执行的目标或者是命令 -->
<executions>
<execution>
<!-- 生命周期的一个阶段 -->
<phase>verify</phase>
<!-- 要执行的目标(命令) -->
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
继承与聚合
如果我们想一次构建多个项目模块,那我们就需要对多个项目模块进行聚合。
继承为了消除重复,我们把很多相同的配置提取出来。
-
聚合主要为了快速构建项目
-
继承主要为了消除重复
我们可以总结为子项目继承父项目的依赖,父项目将子项目聚合在一起。
聚合
这些配置在父项目上
<modules>
<!-- 这里是相对路径 -->
<module>../Hello</module>
<module>../HelloFriend</module>
<module>../MakeFriends</module>
</modules>
定义属性
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.9</junit.version>
<maven.version>0.0.1-SNAPSHOT</maven.version>
</properties>
通过${}访问属性,例如:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
可以用dependencyManagement
控制依赖不被子module共享的方式,但可以做到版本控制
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.itcast.maven</groupId>
<artifactId>HelloFriend</artifactId>
<version>${maven.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
需要注意父工程的packing属性为pom
<!-- 父工程的packing是pom -->
<packaging>pom</packaging>
- dependencyManagement中定义的依赖子module不会共享,子项目需要自己配置,但是不需要配置版本,可以说用这个配置可以实现版本控制
- dependencies中定义的依赖子module可以共享,子module可与不必配置父工程已经有的依赖
继承
通过parent实现对父工程的继承
<parent>
<groupId>com.rl.ecps</groupId>
<artifactId>ecps-parent0315</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 父工程的pom.xml的相对路径 -->
<relativePath>../pom.xml</relativePath>
</parent>
需要注意子工程不需要配置坐标的groupId和项目的版本号version
<!-- pom的版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 坐标第一个维度,组织名.项目名 -->
<!-- <groupId>com.rl.ecps</groupId> -->
<!-- 第二维度,模块名 -->
<artifactId>ecps-console</artifactId>
<!-- 项目打包后的类型 -->
<packaging>war</packaging>
<!-- 第三维度,项目的版本号 -->
<!-- <version>0.0.1-SNAPSHOT</version> -->
如果父工程使用dependencyManagement来做的依赖管理,那么子工程依赖父工程已经有的依赖就不需要配置版本
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock</artifactId>
<scope>test</scope>
</dependency>