1 什么是Maven
Maven对项目进行模型抽象,充分运用的面向对象的思想,Maven可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。Maven 除了以程序构建能力为特色之外,还提供高级项目管理工具。简单的来说Maven是一个项目管理工具,它包含了一个项目对象模型(POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),用来运行在生命周期阶段中插件目标的逻辑。
2 为什么要使用maven
使用maven主要有2点:依赖管理和生命周期
(1)依赖管理:对第三方的jar包进行统一的管理
①添加第三方jar包:开发时使用第三方jar包最简单的方法就是复制粘贴到WEB-INF目录下的lib目录下,每个新工程都要复制粘贴,从而造成工作区中存在大量重复的文件。通过maven只需要维护一个文本形式的jar包的引用,称之为“坐标”,自动从maven仓库中下载到工程(体现了代码重用的原则)
②jar包之间的依赖关系:jar包往往不是孤立存在的,很多jar包都需要在其他jar包的支持下才能够正常工作,Maven就可以替我们自动的将当前jar包所依赖的其他所有jar包全部导入进来,无需人工参与不必了解这个依赖关系。
③jar包之间的冲突:前面是jar包少了无法工作,有时jar包多了项目仍然无法正常工作,Maven中内置了两条依赖原则:最短路径者优先和先声明者优先
(2)生命周期
生命周期的一键构建,提供了一套对项目生命周期管理的标准,开发人员,测试人员统一使用maven进行项目的构建.项目的生命周期管理:编译,测试,打包,部署,运行
3 Maven的核心概念
核心概念包括以下几个方面:POM,约定的目录结构,坐标,依赖,仓库,生命周期,插件和目标,继承,聚合
3.1 POM
Project Object Model:项目对象模型。将Java工程的相关信息封装为对象作为便于操作和管理的模型。Maven工程的核心配置,配置Maven就是pom.xml文件中的配置。
3.2 约定的目录结构
JavaEE开发领域普遍认同一个观点:约定>配置>编码。意思就是能用配置解决的问题就不编码,能基于约定的就不进行配置。而Maven正是因为指定了特定文件保存的目录才能够对我们的Java工程进行自动化构建。 目录结构含义参见前面的描述。
3.3 坐标
Maven的坐标使用三个向量在Maven的仓库中唯一的确定一个Maven工程: ①groupId:公司或组织的域名倒序+当前项目名称 ②artifactId:当前项目的模块名称 ③version:当前模块的版本
<groupId>com.quinto</groupId><artifactId>rtwd-parent</artifactId><version>1.0-SNAPSHOT</version>
将上面3个向量连起来:com.quinto.rtwd-parent的字符串作为目录结构到仓库中查找:com/quinto/rtwd-parent/1.0-SNAPSHOT/rtwd-parent-1.0-SNAPSHOT.jar 注意:自己的Maven工程必须执行安装操作才会进入仓库。安装的命令是:mvn install
3.4 依赖
依赖:当A jar包需要用到B jar包中的类时,我们就说A对B有依赖。
当前工程会到本地仓库中根据坐标查找它所依赖的jar包,配置的基本形式是使用dependency标签指定目标jar包的坐标。如下:
<dependencies><dependency><!--坐标--><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><!--依赖的范围--><scope>test</scope></dependency>
</dependencies>
直接依赖和间接依赖:如果A依赖B,B依赖C,那么A→B和B→C都是直接依赖,而A→C是间接依赖。
3.4.1 依赖的范围
(1)compile ①main目录下的Java代码可以访问这个范围的依赖 ②test目录下的Java代码可以访问这个范围的依赖 ③部署到Tomcat服务器上运行时要放在WEB-INF的lib目录下 如:对主程序的依赖。主程序、测试程序和服务器运行时都需要用到。
(2)test ①main目录下的Java代码不能访问这个范围的依赖 ②test目录下的Java代码可以访问这个范围的依赖 ③部署到Tomcat服务器上运行时不会放在WEB-INF的lib目录下 如:对junit的依赖。仅仅是测试程序部分需要。
(3)provided ①main目录下的Java代码可以访问这个范围的依赖 ②test目录下的Java代码可以访问这个范围的依赖 ③部署到Tomcat服务器上运行时不会放在WEB-INF的lib目录下 如:servlet-api在服务器上运行时,Servlet容器会提供相关API,所以部署的时候不需要。
(4)其他:runtime、import、system等。
各个依赖范围的作用可以概括为下图:
3.4.2 依赖的传递性
当存在间接依赖的情况时,主工程对间接依赖的jar只有依赖范围为compile时可以访问
3.4.3 依赖的原则
(1)路径最短者优先:
(2)路径相同时先声明者(dependency标签配置的先后顺序)优先:
3.4.4 排除依赖
如果直接引入的依赖A,B都有传递依赖于C,而且对C依赖的版本不一样,那就有可能在运行时产生依赖冲突,可以通过排除掉一些传递依赖来避免冲突,如下
<dependency><groupId>org.apache.spark</groupId><artifactId>spark-core_2.12</artifactId><version>3.0.0</version><!--依赖排除--><exclusions><!--需要排除的依赖可以写多个--><exclusion><groupId>org.apache.hadoop</groupId><artifactId>hadoop-client</artifactId></exclusion></exclusions></dependency>
3.4.5 统一管理jar包版本
通过properties标签可以声明自定义属性,在pom中别的地方就可以用${属性名}引用属性的值,如下
<properties><!-- junit --><junit.version>4.12</junit.version><!-- logger --><log4j.version>2.11.0</log4j.version></properties> <dependencyManagement><dependencies><!-- junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency><!-- log4j-core --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>${log4j.version}</version></dependency></dependencies></dependencyManagement>
3.5 仓库
(1)分类
本地仓库 : 相当于缓存。本地仓库存储在本地的磁盘当中,里面存放的是经常使用的jar包。
远程仓库 :
①私服:架设在当前局域网环境下,基本每个公司都会有自己的私服,当本地的磁盘中不存在需要的jar包资源,则会请求私服进行资源下载。
②中央仓库 : 远程仓库,架设在Interne上,由jar专业团队统一维护。
③中央仓库的镜像:架设在各个大洲,为中央仓库分担流量。减轻中央仓库的压力,同时更快的响应用户请求。
在pom.xml中配置仓库的位置
<repositories><repository><id>ali-repo</id><name>ali-repo</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url></repository><repository><id>mvn-repo</id><name>mvn-repo</name><url>https://mvnrepository.com</url><layout>default</layout></repository></repositories>
(2)仓库中的文件 ①Maven的插件 ②我们自己开发的项目的模块 ③第三方框架或工具的jar包
3.6 生命周期
(1)Maven生命周期 Maven生命周期定义了各个构建环节的执行顺序,有了这个清单,Maven就可以自动化的执行构 建命令了。 Maven有三套相互独立的生命周期,分别是: ①Clean Lifecycle在进行真正的构建之前进行一些清理工作。②Default Lifecycle构建的核心部分,编译,测试,打包,安装,部署等等。③Site Lifecycle生成项目报告,站点,发布站点。 它们是相互独立的,可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点。当然你也可以直接运行 mvn clean install site 运行所有这三套生命周期。
每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。比如,运行mvn clean,这个clean是Clean生命周期的一个阶段。有Clean生命周期,也有clean阶段。
(2)clean生命周期 Clean生命周期一共包含了三个阶段:①pre-clean 执行一些需要在clean之前完成的工作 ②clean 移除所有上一次构建生成的文件 ③post-clean 执行一些需要在clean之后立刻完成的工作
(3)Site生命周期 ①pre-site 执行一些需要在生成站点文档之前完成的工作②site 生成项目的站点文档③post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备④site-deploy 将生成的站点文档部署到特定的服务器上 这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点,这可是Maven相当强大的功能,Manager比较喜欢,文档及统计数据自动生成,很好看。
(4)Default生命周期 Default生命周期是Maven生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里,只解释一些比较重要和常用的阶段:
validate
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如JAR。
pre-integration-test
integration-test
post-integration-test
verify
install将包安装至本地仓库,以让其它项目依赖。
deploy将最终的包复制到远程的仓库,以让其它开发人员与项目共享或部署到服务器上运行。
(5)生命周期与自动化构建 运行任何一个阶段的时候,它前面的所有阶段都会被运行,如运行mvn install 的时候,代码会被编译,测试,打包。这就是Maven为什么能够自动执行构建过程的各个环节的原因。此外,Maven的插件机制是完全依赖Maven的生命周期的,因此理解生命周期至关重要。
3.7 插件和目标
(1)Maven的核心仅仅定义了抽象的生命周期,具体的任务都是交由插件完成的。 (2)每个插件都能实现多个功能,每个功能就是一个插件目标。 (3)Maven的生命周期与插件目标相互绑定,以完成某个具体的构建任务。 如:compile就是插件maven-compiler-plugin的一个功能;pre-clean是插件maven-clean-plugin的一个目标。
<plugins><plugin><!-- 编译说明 --><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.2</version><configuration> <!--作为DOM对象的配置--> <source>1.7</source><target>1.7</target></configuration></plugin></plugins>
这里就是告诉我们的代码使用的是什么jdk版本。<plugin>复制文件<groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><executions><execution><phase>compile</phase> 绑定了目标的构建生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段 <goals> 配置的执行目标 <goal>copy-resources</goal></goals><configuration> <outputDirectory>${project.build.outputDirectory}</outputDirectory><resources><resource><directory>src/main/resources/{dev}</directory></resource></resources><overwrite>true</overwrite></configuration></execution></executions></plugin>
这是拷贝配置信息的一段xml,由于我们有三个环境,所以为了便捷可以直接使用插件,进行拷贝一些配置信息。
<plugin>打包声明<groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>2.6</version><configuration><warName>3hfund</warName><failOnMissingWebXml>false</failOnMissingWebXml><webResources><resource><directory>${basedir}/src/main/webapp</directory><filtering>true</filtering><includes><include>WEB-INF/web.xml</include></includes></resource></webResources></configuration>
</plugin>
使用了maven-war-plugin插件进行打包。
<profile> 可以通过 -P ID 来激活<id>PUB</id> ID 标识符<properties><env>pub</env> properties 定义 key-value, 这里 key 是 env, value 是 PROD</properties><activation><activeByDefault>true</activeByDefault> </activation>
</profle>
定义了打包过程中使用的配置文件,打包使用 mvn package –P PUB则打包配置文件是pub的war包。
3.8 继承
使用继承机制就可以将这样的依赖信息统一提取到父工程模块中进行统一管理。
3.8.1 父子工程
创建父工程,父工程只需要保留pom.xml文件即可
<groupId>com.quinto</groupId><artifactId>rtwd-parent</artifactId><!--父工程的打包方式为pom--><packaging>pom</packaging><version>1.0-SNAPSHOT</version>
父工程中的pom会包含module的定义,如下
<modules><module>rtwd-common</module><module>rtwd-canal</module><module>rtwd-etl</module><module>rtwd-simulator</module></modules>
子工程中会有父工程的定义
<parent><artifactId>rtwd-parent</artifactId><groupId>com.quinto</groupId><version>1.0-SNAPSHOT</version></parent>
3.8.2 依赖继承
父工程引入的依赖,所有子工程都会自动继承
3.8.3 依赖管理
父工程通过dependencyManager声明依赖的相关属性(版本),但是并不会真正引入依赖。子工程在引入dependencyManager声明的依赖时,不需要指定版本,直接继承dependencyManager中声明的版本
父工程中声明如下
<dependencyManagement><dependencies><!-- junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency></dependencyManagement>
子工程中引入依赖不需要指定版本和范围
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency></dependencies>
3.9 聚合
将多个工程拆分为模块后,需要手动逐个安装到仓库后依赖才能够生效。修改源码后也需要逐个手动进行clean操作。而使用了聚合之后就可以批量进行Maven工程的安装、清理工作。
配置:在总的聚合工程中使用modules/module标签组合,指定模块工程的相对路径即可。Maven可以根据各个模块的继承和依赖关系自动选择安装的顺序
<modules><module>rtwd-common</module><module>rtwd-canal</module><module>rtwd-etl</module><module>rtwd-simulator</module></modules>
4 Maven安装
(1)检测java环境
Maven是使用Java开发的,所以必须知道当前系统环境中JDK的安装目录。
>echo %JAVA_HOME%
D:\soft\jdk1.8
(2)解压Maven的核心程序
将maven的安装包解压到非中文无空格目录下
(3)配置环境变量
将解压后的路径配置到系统环境变量中
(4)查看是否安装正确
>mvn -v
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: E:\working\maven\apache-maven-3.6.3\bin\..
Java version: 1.8.0_181, vendor: Oracle Corporation, runtime: D:\soft\jdk1.8\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
(5)Maven的核心配置
Maven的核心配置文件位置:Maven安装目录的conf目录下的settings.xml
Maven核心程序查找插件顺序:本地仓库-->远程中央仓库下载(不能上网则无法执行Maven的具体功能)
Maven默认的本地仓库:当前用户的家目录.m2\repository目录
修改settings.xml设置本地仓库位置及配置阿里云镜像
<localRepository>E:\working\maven\maven-repository</localRepository><mirrors><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url></mirror></mirrors>
5 Maven常用的命令(生命周期)
mvn clean命令; 清除target目录
mvn compile命令; 编译,生成target目录
mvn test命令; 将测试代码和核心代码一起编译
mvn package命令; 编译测试代码和核心代码并且打包对应的包类型
mvn install命令; 编译测试代码和核心代码并且打包对应的包类型并且将生成的包安装到本地仓库
mvn deploy命令; 用于发布项目
6 pom.xml配置实例
父工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion>
<groupId>com.quinto</groupId><artifactId>rtwd-parent</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>rtwd-common</module></modules><repositories><repository><id>ali-repo</id><name>ali-repo</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url></repository></repositories><properties><!-- project compiler --><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.encoding>UTF-8</maven.compiler.encoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format><!-- sdk --><java.version>1.8</java.version><scala.version>2.11</scala.version><!-- junit --><junit.version>4.12</junit.version><!-- logger --><log4j.version>2.11.0</log4j.version><slf4j.version>1.7.25</slf4j.version><!-- bigdata --><hadoop.version>2.6.0</hadoop.version><hbase.version>1.2.0</hbase.version><protobuf.version>3.4.0</protobuf.version><flink.version>1.10.0</flink.version><kafka.client.version>1.0.0</kafka.client.version><kafka.version>0.11.0.2</kafka.version><canal.version>1.0.24</canal.version><!-- json --><fastjson.version>1.2.44</fastjson.version>
</properties>
<dependencyManagement><dependencies><!-- junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency><!-- log4j-core --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>${log4j.version}</version></dependency><!-- log4j-api --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>${log4j.version}</version></dependency><!-- log4j-web --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-web</artifactId><version>${log4j.version}</version></dependency><!-- slf4j-api --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><!-- jcl-over-slf4j --><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>${slf4j.version}</version><scope>runtime</scope></dependency><!-- log4j-slf4j-impl --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>${log4j.version}</version></dependency><!-- hadoop --><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-common</artifactId><version>${hadoop.version}-${cdh.version}</version></dependency><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-hdfs</artifactId><version>${hadoop.version}-${cdh.version}</version></dependency><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-client</artifactId><version>${hadoop.version}</version></dependency><!--hbase-shaded , 用来更改hbase中的一些报名,解决protobuf的冲突问题--><dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-shaded-client</artifactId><version>1.2.1</version></dependency>
<dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-shaded-server</artifactId><version>1.2.1</version></dependency><dependency><groupId>org.apache.htrace</groupId><artifactId>htrace-core</artifactId><version>3.1.0-incubating</version></dependency><!-- flink --><dependency><groupId>org.apache.flink</groupId><artifactId>flink-core</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-scala_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-runtime_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-kafka-0.11_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-planner_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-scala_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-java_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-hbase_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.bahir</groupId><artifactId>flink-connector-redis_${scala.version}</artifactId><version>1.0</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-statebackend-rocksdb_${scala.version}</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-cep_${scala.version}</artifactId><version>${flink.version}</version></dependency><!-- kafka --><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka_2.11</artifactId><version>${kafka.version}</version></dependency><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>${kafka.client.version}</version></dependency><!-- canal --><dependency><groupId>com.alibaba.otter</groupId><artifactId>canal.common</artifactId><version>${canal.version}</version></dependency><dependency><groupId>com.alibaba.otter</groupId><artifactId>canal.client</artifactId><version>${canal.version}</version></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency></dependencies></dependencyManagement>
<build><finalName>${project.name}</finalName><plugins><!-- 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>${maven-compiler-plugin.version}</version><!--代码使用的jdk版本--><configuration><!-- 字符集编码 --><encoding>UTF-8</encoding><!-- 源代码使用的JDK版本 --><source>1.8</source><!-- 需要生成的目标class文件的编译版本 --><target>1.8</target><verbose>true</verbose><!-- 要使compilerVersion标签生效,还需要将fork设为true,用于明确表示编译版本配置的可用 --><fork>true</fork></configuration></plugin></plugins></build>
</project>
子模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>rtwd-parent</artifactId><groupId>com.quinto</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion>
<artifactId>rtwd-common</artifactId><dependencies><dependency><groupId>com.alibaba.otter</groupId><artifactId>canal.client</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId></dependency><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency></dependencies>
<build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.4.1.Final</version></extension></extensions><plugins><!-- Protobuf插件 --><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot><protocArtifact>com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}</protocArtifact></configuration><executions><execution><goals><goal>compile</goal></goals></execution></executions></plugin></plugins></build>
</project>