之前匆匆学了maven,没有记好笔记,结果实习中挺多问题还是得百度解决,今天就系统的回顾一下maven。
一、为什么要学Maven?
统一管理依赖
我们现在的项目可能要用到很多框架,项目中依赖的jar包也越来越多,而且jar包之间的依赖关系也很复杂,使用maven可以自动的下载这些jar包并引用、可以很好的自动管理jar包之间的依赖,程序员可以省去很多工作。
进行项目构建
可以简化项目的构建过程。当我们使用IDEA进行开发时,项目的构建都是IDEA进行自动完成的,但是如果脱离了IDE就要用专业的构建工具来完成。
什么是项目的构建?
使用原材料生产出一个产品的过程。
原材料:Java源代码、配置文件、图片等我们开发的东西。
产品:一个可以运行在服务器上的项目。
构建过程包含的主要的环节:
- 清理:删除上一次构建的结果,为下一次构建做好准备
- 编译:Java 源程序编译成 *.class 字节码文件
- 测试:运行提前准备好的测试程序
- 报告:针对刚才测试的结果生成一个全面的信息
- 打包
- Java工程:jar包
- Web工程:war包
- 安装:把一个 Maven 工程经过打包操作生成的 jar 包或 war 包存入 Maven 仓库
- 部署
- 部署 jar 包:把一个 jar 包部署到 Nexus 私服服务器上
- 部署 war 包:借助相关 Maven 插件(例如 cargo),将 war 包部署到 Tomcat 服务器上
idea工具maven的Lifecycle功能讲解
clean:用于清除上次构建生成的所有文件,一般在构建前执行。执行该命令会删除项目路径下的target文件,但是不会删除本地的maven仓库已经生成的jar文件。
mvn clean
validate:验证,验证工程是否正确,所需的信息是否完整。
compile:编译,编译Java源代码成class字节码文件,编译后的class文件会存在target目录下的classes目录中。
主程序编译:mvn compile
测试程序编译:mvn test-compile
主体程序编译结果存放的目录:target/classes
测试程序编译结果存放的目录:target/test-classes
test:测试,用合适的测试框架来进行测试,测试compile中编译出来的代码,测试的东西一般不加包和部署。
mvn test
测试的报告存放的目录:target/surefire-reports
package:打包,将工程文件打包为指定的格式,例如JAR,WAR等(项目的pom文件中的packing标签指定打包类型)。存到target目录,并且拥有compile命令的功能进行编译。如果a项目依赖于b项目,打包b项目时,只会打包到b项目下target下,编译a项目时就会报错,因为找不到所依赖的b项目,说明a项目在本地仓库是没有找到它所依赖的b项目,这时就用到install命令。
verify:验证,检查package是否有效、符合标准。
install:安装,将项目的包安装到本地仓库中(本地仓库中的目录就是项目的坐标),好让其他项目引用。这个命令包含package,也会将项目打包成jar包或war包到本地仓库中。另外,安装操作还会将 pom.xml 文件转换为 XXX.pom 文件一起存入本地仓库。所以我们在 Maven 的本地仓库中想看一个项目的原始的 pom.xml 文件时,查看对应 XXX.pom 文件即可。
site:站点,生成项目的站点文档,生成的网站是在项目的“target/site”文件夹。
deploy:部署,复制最终的包至远程仓库,共享给其它开发人员和项目。
二、如何学好maven
maven安装
安装很简单,就不说了,主要记录一下maven目录下的conf/settings.xml设置文件
指定本地仓库位置
本地仓库默认值:用户家目录/.m2/repository。由于本地仓库的默认位置是在用户的家目录下,而家目录往往是在 C 盘,也就是系统盘。将来 Maven 仓库中 jar 包越来越多,仓库体积越来越大,可能会拖慢 C 盘运行速度,影响系统性能。所以建议将 Maven 的本地仓库放在其他盘符下。
<localRepository>D:\maven-repository</localRepository>
配置阿里云提供的镜像仓库
项目依赖的jar包如果本地仓库中没有,就会远程仓库中找,下载 jar 包默认访问境外的中央仓库,而国外网站速度很慢。改成阿里云提供的镜像仓库,访问国内网站,可以让 Maven 下载 jar 包的时候速度更快。配置的方式是:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
配置jdk版本
在settings.xml文件中设置会在所有maven项目中都生效,在pom文件中的配置只对当前项目有效。
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
maven的工作机制
在项目的pom文件中定义好依赖的坐标后,先到本地仓库中找有没有,如果没有的话,就到私服中找,如果私服有则直接下载到本地仓库中用,反之则到远程仓库中下载,jar包下载后缓存在私服中(方便使用),最后都将jar包保存到本地仓库中使用。
maven的私服
maven的私有服务器,称为私服,是一种特殊的远程仓库,一般在公司中会自己的局域网内搭建自己的私服。
因为公司中一般会有很多人同时在开发一些项目,如果每个人都去远程仓库下载依赖,那么网络负载就会比较大, 在自己的局域网内搭建了私服,然后常用的依赖上传到私服中,下载访问的速度肯定比较快(提高jar包下载速度)。
如何配置私服:
<!--镜像库nexus_coracle 和第三方库thirdparty kevin 20180719-->
<repositories>
<repository>
<id>nexus_coracle</id>
<name>coracle private nexus</name>
<url>http://xxx:7081/nexus/content/groups/public</url>
</repository>
<repository>
<id>thirdparty</id>
<name>Repository for thirdparty</name>
<url>http://xxx:7081/nexus/content/repositories/thirdparty</url>
</repository>
</repositories>
maven依赖的范围
标签的位置:dependencies/dependency/scope
范围可选值:compile/test/provided/system/runtime/import
默认值:compile
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
有效 | 有效 | 有效 | 有效 | compile |
无效 | 有效 | 有效 | 无效 | test |
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
有效 | 有效 | 有效 | 有效 | compile |
有效 | 有效 | 有效 | 有效 | provided |
总结:
compile:通常使用的第三方框架的 jar 包,项目在实际运行时要用到的 jar 包都是以 compile 范围进行依赖的,就是需要参与部署到服务器上的。比如 SSM 框架所需jar包。
test:测试过程中使用的 jar 包,以 test 范围依赖进来,也不会打包进去,不参与部署。比如 junit。
provided:在开发过程中需要用到的“服务器上的 jar 包”通常以 provided 范围依赖进来。比如 servlet-api、jsp-api,这个服务器上已经有了,tomcat服务器本身里面就有了。而这个范围的 jar 包之所以不参与部署、不放进 war 包,就是避免和服务器上已有的同类 jar 包产生冲突,同时减轻服务器的负担。说白了就是:“服务器上已经有了,你就别带啦!”
maven依赖的传递性
在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。
- B 依赖 C 时使用 compile 范围:可以传递
- B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。
maven依赖冲突的排除
当 A 依赖 B,B 依赖 C 而且 C 可以传递到 A 的时候,A 不想要 C,需要在 A 里面把 C 排除掉。而往往这种情况都是为了避免 jar 包之间的冲突。
A项目依赖了B(B依赖了X-1.0),A项目也依赖了C(C依赖了X-2.0),如果X-都能传递到C的话,这样两个X-就会冲突,所以需要排除掉一个,解决包冲突。
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
<!-- 使用excludes标签配置依赖的排除 -->
<exclusions>
<!-- 在exclude标签中配置一个具体的排除 -->
<exclusion>
<!-- 指定要排除的依赖的坐标(不需要写version) -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
maven的继承
Maven工程之间,A 工程继承 B 工程
- B 工程:父工程
- A 工程:子工程
本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置。
意义:
在父工程中统一管理项目中的依赖信息,具体来说是管理依赖信息的版本。
一个项目比较大的,通常要分成好几个模块,每个模块都会维护自己的依赖信息,有些依赖可能都是一样的,使用继承可以统一依赖的版本信息,统一依赖的最优版本组合,通过父工程统一管理维护依赖的信息的组合,可以保证依赖使用的准确性。
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
<packaging>pom</packaging>
<!-- 使用dependencyManagement标签配置对依赖的管理 -->
<!-- 被管理的依赖并没有真正被引入到工程 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
<!-- 父工程的坐标 -->
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- 子工程的坐标 -->
<!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
<!-- <groupId>com.atguigu.maven</groupId> -->
<artifactId>pro04-maven-module</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->
<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。 -->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的dependencyManagement来决定。 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
</dependencies>
编写一套符合要求、开发各种功能都能正常工作的依赖组合并不容易。如果公司里已经有人总结了成熟的组合方案,那么再开发新项目时,如果不使用原有的积累,而是重新摸索,会浪费大量的时间。为了提高效率,我们可以使用工程继承的机制,让成熟的依赖组合方案能够保留下来。
如上图所示,公司级的父工程中管理的就是成熟的依赖组合方案,各个新项目、子系统各取所需即可。