1. 概念
1.1 官网:
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开源项目;
- Java语言开发(使用时依赖JDK);
- 基于
工程对象模型(pom)
概念的项目构建工具。
1.2 两大核心:
- 依赖管理:对Jar包的统一管理(项目源码非常小);
- 项目构建:通过命令实现项目测试、编译、打包、安装、部署等(开发中可能使用Eclipse或Idea完成该系列功能);
1.3 maven下载安装
-
下载:新版本maven依赖1.7及以上JDK;
-
选择文件:选择zip即可;
-
解压:
sudo unzip apache-maven-3.6.2-bin.zip -d /Library/
-
安装:https://maven.apache.org/install.html;
vim ~/.bash_profile
;- 添加:
export PATH=${PATH}:/Library/apache-maven-3.6.2/bin
,保存; source ~/.bash_profile
;- 验证:
mvn -v
。
注意:如果使用压缩包安装jdk,确保JAVA_HOME也已配置。
1.4 仓库:
- 仓库:就是存放Jar包的地方:
- 中央仓库(https://mvnrepository.com):公网上存放所有jar包(存在版权的例外),联网可下载Jar包;
- 私服:一般只公司配置的局域网私服;
- 避免多人重复从中央仓库下载资源;
- 存放第三方Jar包;
- 本公司开发代码(Jar)存放;
- 本地仓库:无论从私服还是中央仓库,打包时都要先将使用的Jar包下载到本地仓库。
- 配置本地仓库(默认:
${user.home}/.m2/repository
):- 在settings.xml添加:
<localRepository>/Users/gp/.m2/repo</localRepository>
;
- 在settings.xml添加:
1.5 maven项目结构
-
使用Idea创建maven项目:
-
File
—>new
—>Project
—>maven
—>next
—> 填写GroupId、ArtifactIdd和Version —>next
—> 填写Project Name和Project Location —>Finish
。 - maven项目结构:
```
- src:源码
- main:源码文件夹
- java:java类
- resources:配置文件
- test:测试文件夹
- java:测试类
- main:源码文件夹
- pom.xml:核心配置文件 ``` 说明:只要保持该结构,手动创建文件(夹)也可创建maven项目。
- src:源码
-
编码:在src/main/java文件夹下创建包及编码
-
编写测试代码:
- 在pom.xml中添加Junit依赖:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> </dependencies>
-
测试代码:
2. 项目构建
maven支持通过命令来构建项目。打开终端,在项目的pom.xml所在路径下执行。
-
mvn compile
:编译。首次执行时会从中央仓库下载依赖的相关Jar包到配置的本地仓库,最后提示BUILD SUCCESS
。查看目录结构,编译后增加了target文件夹:说明:通过查看终端打印信息可以看出,maven命令其实是通过调用相应Jar包完成编译等相应任务的。编译命令不会编译单元测试文件。
-
mvn clean
:将target文件夹清除。执行命令后,target文件夹已被删除; -
mvn test
: 单元测试说明:可以看到,执行
test
命令会先执行compile
。mvn test
会把单元测试方法全部执行(测试类也编译);注意:要想通过
mvn
命令执行单元测试,类名必须以’Test’开头或结尾。 -
mvn package
:打包。通过打印信息可以看出,打包命令会先编译,还会执行单元测试,然后在target目录下会生成Jar包。 -
mvn install
:安装,将项目打包并安装到本地仓库。说明:安装会执行编译、测试、打包。如果有修改或更新,编写完代码后直接再次执行
mvn install
即可覆盖更新。
注意:
-
使用
mvn
命令打包或安装生成的Jar包不会包含pom中的依赖包。可以使用Idea的Build Artifacts
打包。也可以使用其它插件完成,如assembly:mvn assembly:assembly
。... <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build>
-
如果要打包为war包,则在pom.xml中添加
<packaging>war</packaging>
,并添加相应的web.xml。web项目使用mvn package
打包时会包含pom中的依赖包。 -
一般通过在pom文件中设置插件的编译版本,否则如Eclipse或Idea默认构建时使用JAVA1.5版本,Eclipse甚至直接报错:
... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>
3. 依赖管理
3.1 依赖范围
-
引入:
通过Idea新建一个maven web项目,创建包并新建一个Servlet,会报错:
原因为依赖的servlet-api包不存在。现在我们通过maven添加一个servlet-api依赖(既然是maven项目就别添加lib了):
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency>
点击提示的
Import Changes
,报错消失(或者选择Enable Auto-Import
)。编译没有任何问题,但当我们通过
mvn tomcat:run
运行项目时,访问servlet会报错:原因为tomcat本身的lib包中是包含servlet-api.jar的(手动将war放入tomcat/apps下运行tomcat未报错,但问题肯定还是存在的):
因此为使编译时正常,打包时又不会把不需要的Jar包打入,在pom文件中引入时,需要添加Jar包的依赖范围
<scope></scope>
:<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency>
-
依赖范围:
依赖范围 编译 测试 运行 例子 compile Y Y Y 大部分包 test - Y - Junit provided Y Y - servlet-api runtime - Y Y JDBC驱动(反编译) system Y Y - 仓库外的包 说明:pom文件默认依赖范围为compile。如果一个依赖有scope,那么该依赖包:
- compile:编译时有效(可以
import
),测试时(src/test下类)有效,打包时会打入依赖包; - test:编译时无法使用(找不到,相当于没有引入),测试时有效,运行时不会打包;
- provided:编译时有效,测试时有效,不会打包;
- compile:编译时有效(可以
3.2 依赖传递
-
引入
如在maven中添加struts2依赖包,则只需要在pom文件中添加struts2-core,struts2-core本身依赖的包会自动添加到项目:
... <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.24</version> </dependency> ....
-
概念:
- 直接依赖:A(项目)的pom文件中直接依赖B,则B为A的直接依赖;
- 传递依赖:如果B本身右依赖相关Jar包C,则C称为A的传递依赖;
-
问题:
- 传递依赖冲突:如果A直接依赖B和D,而B和D都依赖C,但它们依赖C的版本不同,此时会引起依赖冲突问题。
-
依赖冲突解决:
-
maven自己处理原则
- 第一优先原则:A先依赖谁,则以谁的依赖为主。如pom文件中D在B前面,则C的版本为D依赖的版本;
- 路径近优先:如果在A的pom文件中直接依赖C,则以该直接依赖版本为主,不再依赖传递依赖;
-
排除依赖:手动将A的直接依赖B下面传递依赖中不想要的排除;
... <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> <!-- 手动将不想要的依赖排除 --> <exclusions> <exclusion> <artifactId>spring-beans</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.1.9.RELEASE</version> </dependency> ...
-
版本锁定(推荐):直接通过
<dependencyManagement>
节点控制版本(该节点只控制版本,本身不会导入依赖)。... <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.1.9.RELEASE</version> </dependency> </dependencies> </dependencyManagement> ...
说明:版本信息可以先通过
<properties>
节点定义,使用时通过ognl表达式引用,这也可以统一控制:... <properties> <spring.beans.version>4.2.4.RELEASE</spring.beans.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.beans.version}</version> </dependency> </dependencies> </dependencyManagement> ...
注意:实际开发中最好使依赖之间版本统一,或者确保传递依赖版本不影响使用,否则可能出现问题。因为理论上版本不一致是存在冲突的:
-
3.3 依赖传递范围
依赖传递并不会无休止传递,通过依赖范围控制传递的范围。
compile | provided | runtime | test | |
---|---|---|---|---|
compile | compile | - | runtime | - |
provided | provided | provided | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
说明:纵坐标为直接依赖(B对A的范围),横坐标为传递依赖(C对B的范围)。如B中C的范围为test时,若A直接依赖B,则并不会引入C。