文章目录
maven作为一个构建工具,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的操作接口。
中央仓库:
http://repo1.maven.org/maven2/
约定:
默认打包主目录:src/main/java
默认打包测试目录:src/test/java
一、坐标说明
<groupId>mavenStudy</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
----case---
<groupId>com.xxx.cb.web</groupId>
<artifactId>cb-web-boss</artifactId>
<version>1.1.0</version>
<scope>system</scope>
<packaging>war</packaging>
- groupId:当前Maven项目隶属的实际项目。通常一个项目会有很多模块;groupId不应该对应项目隶属的组织或公司,因为组织或公司会有很多实际项目。
- artifactId:实际项目中的一个maven项目(模块),推荐使用实际项目名称作为前缀。
- version:当前项目的版本。maven定义了一套完整的版本规范,以及快照(SNAPSHOT)概念。
- packaging:maven项目的打包方式。打包方式会影响到构建的生命周期,比如jar打包和war打包会使用不同的命令。不定义时maven默认打包为jar;
- scope:依赖范围
- compile:编译依赖范围。如果没有指定,就会默认该范围。该范围,对于编译、测试、运行三种classpath都有效。
- test:测试依赖范围。此范围,只对于测试classpath有效,典型的例子是JUnit。
- provided:已提供依赖范围。此范围,对于编译和测试classpath有效,但在运行时无效。
- runtime:运行时依赖范围。此范围,对于测试和运行classpath有效,但在编译时无效。
- system:系统依赖范围。此范围与provided完全一致。但是必须通过systemPath元素显示地指定依赖文件的路径。一般情况下,本地依赖,例如在项目某个文件夹下的本地依赖,比如外来sdk,我们通过这个依赖来指定它的路径。
- optional:标记依赖是否可选。作为可选依赖,他们只会对当前项目A产生影响,当其他项目依赖项目A时,这个依赖就不会传递下去。备注:在理想的情况下是不应该使用可选依赖的。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.6.RELEASE</version>
<optional>true</optional>
</dependency>
- exclusions:排除传递性依赖。例如在该依赖中,poi已经依赖了commons-codec,但是我们并不想引入这个传递性依赖,所以使用进行排除…表示这个依赖通过传递,而是自己从另外的提供,或者不提供。可声明多个子项目,排除多个。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
</exclusions>
</dependency>
二、标签
- distributionManagement:将本项目发布到仓库:在config中配置对应id的仓库,该项目使用发布命令,就能发布到对应仓库
<distributionManagement>
<repository>
<id>dhcao-repoReleases</id>
<name>Proj Release Repository</name>
<url>http://ddddddddddddddd/content/repositories/proj-releases</url>
</repository>
<snapshotRepository>
<id>dhcao-repoSnapshots</id>
<name>Proj Release Repository</name>
<url>http://ddddddddddddddd/content/repositories/proj-snapshots</url>
</snapshotRepository>
</distributionManagement>
- scm标识发布版本的一些信息;利用release插件打包的时候。这个可以被识别;
发布之后,就会声明scm地址:http://svn.myproj.tech:7000/xxxx_cb/版本号…
<scm>
<url>http://svn.xxxx.tech:7000/xxxx_cb</url>
</scm>
三、仓库
3.1 仓库解析路径:
如果构件:
groupID= org.testing
artifactId = testing
version = 5.8
classifier = jdk15
packaging = jar
生成路径为:org/testing/testing/5.8/testing-5.8。
如果构建有classifer,就加上构件分隔符和classifer,那么路径就变成:org/testing/testing/5.8/testing-5.8-jdk5
3.2 仓库分类
- 本地仓库
- 远程仓库
3.3 仓库配置
<servers>
<server>
<id>maven.xxxx.org</id>
<username>developer</username>
<password>xxxxx@8</password>
</server>
<server>
<id>maven.xxxxx.org-releases</id>
<username>developer</username>
<password>xxxxxx@8</password>
</server>
<server>
<id>maven.xxxxxx.org</id>
<username>developer</username>
<password>xxxxxxx@8</password>
</server>
</servers>
3.3 部署项目到仓库
<distributionManagement>
<repository>
<id>dhcao-repoReleases</id>
<name>Proj Release Repository</name>
<url>http://ddddddddddddddd/content/repositories/proj-releases</url>
</repository>
<snapshotRepository>
<id>dhcao-repoSnapshots</id>
<name>Proj Release Repository</name>
<url>http://ddddddddddddddd/content/repositories/proj-snapshots</url>
</snapshotRepository>
</distributionManagement>
- <repository>:这个是发布版本(正式版本)的仓库,需要跟中配置的id一样。
- <snapshotRepository>:这个是快照版本的仓库,如果项目声明为snapshot版本,就会部署到这里。
- 过程:运行 mvn clean deploy 就会将项目构建时输出的构件部署到对应的远程仓库。
3.4 快照版本
<groupId>mavenStudy</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
这个就是快照版本。maven找到快照版本时,会自动为构件打上时间戳。比如2.1-20211214.221414-13.就代表2021年12月14日22点14分14秒的第13次快照。有了该快照,maven就能随时找到仓库中该构件的最新版本(每次都会找最新)。
3.5 镜像
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。
在<mirrors>下可以配置多个<mirror>镜像
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
- <mirrorOf> * </mirrorOf>:表示他是哪个仓库的镜像,* 表示对所有的仓库的访问都会访问这个镜像。
- <mirrorOf>external:*</mirrorOf>:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说,匹配所有不在本机上的远程仓库
- <mirrorOf>repo1,repo2</mirrorOf>:匹配仓库repo1和仓库repo2,使用逗号分隔多个远程仓库。
- <mirrorOf>*, ! repo1</mirrorOf>:匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。
四、传递性依赖
maven中通常会使用指定依赖的影响范围。
4.1 依赖范围
一、介绍下依赖范围
- scope:依赖范围
- compile:编译依赖范围。如果没有指定,就会默认该范围。该范围,对于编译、测试、运行三种classpath都有效。
- test:测试依赖范围。此范围,只对于测试classpath有效,典型的例子是JUnit。
- provided:已提供依赖范围。此范围,对于编译和测试classpath有效,但在运行时无效。
- runtime:运行时依赖范围。此范围,对于测试和运行classpath有效,但在编译时无效。
- system:系统依赖范围。此范围与provided完全一致。但是必须通过systemPath元素显示地指定依赖文件的路径。一般情况下,本地依赖,例如在项目某个文件夹下的本地依赖,比如外来sdk,我们通过这个依赖来指定它的路径。
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.5</version>
<scope>system</scope>
<systemPath>${basedir}/WebContent/WEB-INF/lib/hamcrest-core-1.3.jar</systemPath>
</dependency>
4.2 传递性依赖
假设,在现在的项目relax中引入了spring-core。例如:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
那么它的默认范围就是compile,在spring-core项目中又依赖了commons-logging。
我们称原有项目relax对于spring-core是第一直接依赖,spring-core对于commons-logging是第二直接依赖。relax对于common-logging是传递性依赖。
(如果A依赖于B,B依赖于C,那么A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖)
4.3 传递性依赖范围
先上图表,再解释
解释这个图表
左边第一列:第一直接依赖范围。
上面第一行:第二直接依赖范围。
图表交汇点:传递性依赖范围。
规律:
- 第二直接范围是compile时,传递性依赖的范围与第一直接依赖的范围一致;
- 第二直接依赖的范围是test的时,依赖不得传递;
- 第二直接依赖的范围是provided时,只传递第一直接依赖范围也为provided的依赖;
- 第二直接依赖的范围是runtime时,传递性依赖范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。
4.4 依赖调解
传递性依赖可能造成,jar包冲突?–不会
例1,项目A有这样的依赖关系:A -> B -> C -> X(1.0),A -> D -> X(2.0)。X有2个版本1.0和2.0,都是A的传递性依赖,如果不加以处理,明显会造成jar包冲突。
那么maven怎么处理呢,
maven依赖调解第一原则:路径个最优者优先。
明显X(1.0)路径长度是3,X(2.0)路径长度是2。所以maven会选择 X(2.0)。
例2,项目A又有了这样的依赖关系:A -> B -> Y(1.0),A -> D -> Y(2.0)。这时路径都是2,。怎么破。在Maven2.0.9版本之后,这个就可以处理了。
maven依赖调解第二原则:第一声明者优先。
在pom文件中,哪个先被声明,就使用哪个。
4.5 排除依赖
已经提过了
传递性依赖会给项目隐式地引入很多依赖,也会出现问题。例如当前项目有一个第三方依赖,而这个第三方依赖由于某些原因依赖了另外一个类库的SNAPSHOT的版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT项目是很不稳定的,会影响到当前的项目。需要排除掉该SNAPSHOT。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
</exclusions>
</dependency>
五、编译插件
5.1 编译插件:
maven-compiler-plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<failOnError>true</failOnError>
<verbose>true</verbose>
<fork>true</fork>
<compilerArgument>-nowarn</compilerArgument>
<source>1.8</source>
<target>1.8</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
5.2 打包main方法插件
maven指定jar包的main方法,来启动项目
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<!-- 指定MANIFEST.MF中的Main-Class -->
<mainClass>com.alibaba.dubbo.container.Main</mainClass>
<!-- 打包时 MANIFEST.MF文件不记录的时间戳版本 -->
<useUniqueVersions>false</useUniqueVersions>
<!-- 在MANIFEST.MF加上Class-Path项并配置依赖包 -->
<addClasspath>true</addClasspath>
<!-- 指定依赖包所在目录 -->
<classpathPrefix>lib/</classpathPrefix>
</manifest>
<manifestEntries>
<Class-Path>.</Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
maven实战书中提供:已测试
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
// 将插件绑定到生命周期 -- package
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>HelloWorld</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
六、 生命周期
6.1 maven三套生命周期
maven有三套相互独立的生命周期,他们分别是clean、default和site。每个生命周期包含一些阶段(phase)
- clean:清理项目
- pre-clean
- clean
- post-clean
- default:构建项目(阶段看后面解释)
- site:建立项目站点
- pre-site:执行一些在生成项目站点之前需要完成的工作
- site:生成项目站点文档
- post-site:执行一些在生成项目站点之后需要完成的工作
- site-deploy:将生成的项目站点发布到服务器上
6.2 default生命周期的阶段(phase)
跟spring的applicationContext的refresh()里面的操作一样,贼多!
- validate
- initialize
- generate-sources
- process-sources:处理项目主资源文件。一般来说,是对src/main/resources目录的内容进行变量替换等工作后,复制到输出的主classpath目录中。
例如:我们根据生产环境配置的文件跟开发环境不一致,maven打包时,发现我们在生产环境,就会将生产环境对应目录的文件复制替换,然后打包到classpath - generate-resources
- process-resources
- compile:编译项目的主源码。一般来说,编译src/main/java目录下的文件到输出的主classpath中
- process-test-sources:处理项目测试资源文件。一般来说,是对src/test/resources目录的内容进行变量替换等工作后,复制到项目输出的测试classpath中
- generate-test-sources
- process-test-resources
- test-compile:编译项目的测试代码。一般来说,是编译…src/test/java
- process-test-classes
- test:使用单元测试框架运行测试,测试代码不会打包或部署
- prepare-package
- package:接受编译好的代码,打包成可发布的格式,如jar
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install:将包安装到Maven本地仓库,供本地其他Maven项目使用。是本地哦
- deploy:将最终的包复制到远程仓库,供其他开发人员和Maven项目使用。
6.3 命令行与生命周期
命令行就是多个生命周期的简单组合
mvn clean:调用clean的clean阶段
mvn test:default的test阶段
mvn test install:clean的clean阶段和default的install阶段
mvn clean deploy site-deploy:调用clean的clean阶段,default的deploy的阶段,site的site-deploy阶段。
6.4 插件绑定
maven的插件跟生命周期相互绑定,可以自定义插件,然后绑定maven的特定生命周期阶段。
例如maven-compiler-plugin插件,就绑定了default生命周期的compile阶段。
如果啥插件都不指定,那么就会使用内置插件绑定:
注意:这里只列出了拥有绑定关系的阶段,default还有很多阶段,默认他们没有绑定任何插件,因此也没有任何实际行为。
6.5 自定义绑定
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
// 任务执行,插件可以执行多个任务
<executions>
<execution>
// 绑定了package阶段,很多插件不需要指定,因为插件的目标在编写时已经定义好默认绑定阶段
<phase>package</phase>
<goals>
// 通过goals配置指定要执行的插件目标
<goal>shade</goal>
</goals>
// 某些插件支持出传入参数,像mvn install -Dmaven.test.skip=true。里面的-D意味着
//后面是参数,真正的参数是maven.test.skip=true,这个也可以使用configuration配置,全局生效。
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>HelloWorld</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
七、版本管理和环境过滤
7.1 maven约定的版本号:
<主版本>.<次版本>.<增量版本>-<里程碑版本>
- 主版本:表示了项目的重大架构变更。例如Maven2和Maven1相去甚远;Struts1和Struts2采用了不同的架构
- 次版本:表示较大范围的功能增加和变化,及bug修复。
- 增量版本:一般表示重大bug的修复。
- 里程碑版本:顾名思义,某一个版本的里程碑。
例如:1.3.4-beta-2
主版本:1;
次版本:3;
增量版本:4;
里程碑版本:beta-2;
7.2 环境过滤
7.2.1 配置
每个<profile>标签就是一个环境配置,使用id进行定位
<!-- 全局属性配置 -->
<profiles>
<!-- 开发环境 -->
<profile>
<id>dev</id>
<properties>
<!-- 部署环境(对应配置文件版本) -->
<env>dev</env>
<suffix>LATEST</suffix>
<maven.test.skip>false</maven.test.skip>
</properties>
<!-- 设置默认环境 -->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 测试预发环境 -->
<profile>
<id>uat</id>
<properties>
<!-- 部署环境(对应配置文件版本) -->
<env>uat</env>
<maven.test.skip>false</maven.test.skip>
</properties>
</profile>
<!-- 测试环境 -->
<profile>
<id>test</id>
<properties>
<!-- 部署环境(对应配置文件版本) -->
<env>test</env>
<suffix>BETA</suffix>
<maven.test.skip>true</maven.test.skip>
</properties>
</profile>
<!-- 生产环境 -->
<profile>
<id>pro</id>
<properties>
<!-- 部署环境(对应配置文件版本) -->
<env>pro</env>
<suffix>RELEASE</suffix>
<maven.test.skip>true</maven.test.skip>
</properties>
</profile>
</profiles>
<resources>
<resource>
<directory>src/main/resources</directory>
<targetPath>${project.build.directory}/classes</targetPath>
// 开启过滤,就是说,类似{project.build.xxxx},{aa.bb.cc}就会生效。不然不生效
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
7.2.2 激活
配置了环境<profile>,那么如何激活,使用呢。
- 命令行激活,使用-P加上profile的id来激活,多个id之间以逗号分开。
mvn clean install -Pdev,test // 激活了dev和test2个环境
- setting文件显示激活:如果用户希望某个profile默认处理激活状态,在maven的setting.xml中配置
<settings>
...
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
...
</setting>
- 系统属性激活:配置当系统属性存在的时候,自动激活profile
<profile>
<activation>
<property>
// 定义了属性anme为test,如果命令行中含有name属性,则激活
<name>test</name>
</property>
</activation>
</profile>
// 如下激活命令
mvn clean install -Dtest
或者
<profile>
<activation>
<property>
// 定义了属性anme为test,如果命令行中含有name属性,则激活
<name>test</name>
<value>xx</value>
</property>
</activation>
</profile>
// 如下激活命令
mvn clean install -Dtest=xx
- 操作系统环境激活
<profile>
<activation>
<os>
<name>Windows XP</name>
// family 包括:windows、unix和mac
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
</activation>
</profile>
- 文件存在与否激活
<profile>
<activation>
<file>
<missing>x.properties</missing>
<exists>y.properties</exists>
</file>
</activation>
</profile>
- 默认激活:可以设置默认激活,如果有任意环境通过以上任意一种方式激活,那么默认激活就会失效。
<profile>
<id>dev</id>
<properties>
<!-- 部署环境(对应配置文件版本) -->
<env>dev</env>
// maven环境变量
<suffix>LATEST</suffix>
<maven.test.skip>false</maven.test.skip>
</properties>
<!-- 设置默认环境 -->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
7.3 Web资源过滤
有的时候,希望在构建项目的时候,为不同的客户使用不一样的资源文件。这时可以在web资源文件中使用Maven属性,例如用${client.logo}表示图片,然后使用不同的profile分别定义这些Maven属性的值
最后需要配置maven-war-plugin对src/main/webapp/这一资源目录进行过滤。