概述
maven的主要竞争者是SBT(全称为Simple build tool,是Scala事实上的标准构建工具,随着Scala兴起),曾经红人的Ant(已经确实日薄西山了),Gradle(也许最终将成为MAVEN的终结者)。
依赖:
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
- maven中,如果忽略version设置,会自动下载最新版本。
- 依赖冲突的调节:如果A->B->C->X(2.0),A->D->X(1.0)。由于只能引入一个版本的包,此时Maven按照最短路径选择导入x(2.0)。如果A->B->X(1.0),A->D->X(2.0)。路径长度一致,则优先选择第一个,此时导入x(1.0)。如果需要指定依赖版本,可以则需要使用exclusion。
- maven中的仓库分为两种,snapshot快照仓库和release发布仓库。snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本。定义一个组件/模块为快照版本,只需要在pom文件中在该模块的VERSION后加上-SNAPSHOT即可(注意这里必须是大写)
Maven的scope
1.compile:默认范围,编译测试运行都有效
2.provided:在编译和测试时有效
3.runtime:在测试和运行时有效
4.test:只在测试时有效
5.system:在编译和测试时有效,与本机系统关联,可移植性差
这些scope主要是为了本地环境编译出的代码不会和线上环境相互冲突。
dependencyManagement
<dependencyManagement>
<dependencies>
<!-- root-pom 配置 -->
<dependency>
<groupId>com.xxx</groupId>
<artifactId>infra-root-pom</artifactId>
<version>${root_pom_version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
依赖管理的优先级低于parent中的dependencyManagement中制定的版本。
比如我们最熟悉的
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
就实际上采用了spring所推荐的版本。
MAVEN声明周期
Maven定义了三套生命周期:clean、default、site,每个生命周期都包含了一些阶段(phase)。三套生命周期相互独立,但各个生命周期中的phase却是有顺序的,且后面的phase依赖于前面的phase。执行某个phase时,其前面的phase会依顺序执行,但不会触发另外两套生命周期中的任何phase。
clean生命周期
pre-clean :执行清理前的工作;
clean :清理上一次构建生成的所有文件;
post-clean :执行清理后的工作
default生命周期
default生命周期是最核心的,它包含了构建项目时真正需要执行的所有步骤。
validate
initialize
generate-sources
process-sources
generate-resources
process-resources :复制和处理资源文件到target目录,准备打包;
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或者war或者其他格式的分发包;
pre-integration-test
integration-test
post-integration-test
verify
install :将打好的包安装到本地仓库,供其他项目使用;
deploy :将打好的包安装到远程仓库,供其他项目使用;
site生命周期
pre-site
site :生成项目的站点文档;
post-site
site-deploy :发布生成的站点文档
MAVEN插件
maven插件可以绑定到上述生命周期中执行。比如下面的例子
<plugin>
<groupId>io.confluent</groupId>
<artifactId>kafka-schema-registry-maven-plugin</artifactId>
<version>4.1.1</version>
<configuration>
<schemaRegistryUrls>
<param>http://10.221.198.127:9096</param>
</schemaRegistryUrls>
<outputDirectory>src/main/resources/avro</outputDirectory>
<subjectPatterns>
<param>UmeEventTest1</param>
</subjectPatterns>
</configuration>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>download</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.8.2</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/resources/avro/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
一个在initialize的phase执行,负责下载文件,一个在generate-sources阶段执行,负责将下载下来的文件改写成java代码。
可选依赖
当一个项目A依赖另一个项目B时,项目A可能很少一部分功能用到了项目B,此时就可以在A中配置对B的可选依赖。举例来说,一个类似hibernate的项目,它支持对mysql、oracle等各种数据库的支持,但是在引用这个项目时,我们可能只用到其对mysql的支持,此时就可以在这个项目中配置可选依赖。
配置可选依赖的原因:1、节约磁盘、内存等空间;2、避免license许可问题;3、避免类路径问题,等等。
示例:
<project>
...
<dependencies>
<!-- declare the dependency to be set as optional -->
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0</version>
<scope>compile</scope>
<optional>true</optional> <!-- value will be true or false only -->
</dependency>
</dependencies>
</project>
假设以上配置是项目A的配置,即:Project-A --> Project-B。在编译项目A时,是可以正常通过的。
如果有一个新的项目X依赖A,即:Project-X -> Project-A。此时项目X就不会依赖项目B了。如果项目X用到了涉及项目B的功能,那么就需要在pom.xml中重新配置对项目B的依赖。
一些常见的插件的介绍
maven-shade-plugin
- 将依赖的jar包打包到当前jar包(常规打包是不会将所依赖jar包打进来的);
- 对依赖的jar包进行重命名(用于类的隔离);
第一个功能是为了我们减少jar的数量,能够给交付方一个单一的jar显然是最好的。但是会同时带来一个问题,就是jar的重复依赖和冲突。所以,我们可以进行重命名,如下列配置
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>org.codehaus.plexus.util</pattern>
<shadedPattern>org.shaded.plexus.util</shadedPattern>
<excludes>
<exclude>org.codehaus.plexus.util.xml.Xpp3Dom</exclude>
<exclude>org.codehaus.plexus.util.xml.pull.*</exclude>
</excludes>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
将将“org.codehaus.plexus.util”重命名为“org.shaded.plexus.util。
profile
在开发过程中,我们的软件会面对不同的运行环境,比如开发环境、测试环境、生产环境,而我们的软件在不同的环境中,有的配置可能会不一样,比如数据源配置、日志文件配置、以及一些软件运行过程中的基本配置,那每次我们将软件部署到不同的环境时,都需要修改相应的配置文件,这样来回修改,很容易出错,而且浪费劳动力。
profile的定义如下
<profiles>
<profile>
<id>dev</id>
<properties>
<env>dev</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>prd</id>
<properties>
<env>prd</env>
</properties>
</profile>
</profiles>
可以理解为,使用profile的机制就是可以动态切换所使用的变量。
常见的使用办法是写在总Pom里,这样可以,像这样
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
...
这样可以在打不同包时使用不同的repo,使用不同的插件等等。
排查问题
mvn dependency:tree -Dverbose > 1.txt
返回的输出类似
[INFO] +- com.xiaohongshu:event-logging-spring-boot-autoconfigure:jar:0.0.7-SNAPSHOT:compile
[INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.6.RELEASE:compile
[INFO] | | \- (org.springframework.boot:spring-boot:jar:2.1.6.RELEASE:compile - omitted for duplicate)
[INFO] | \- org.springframework.cloud:spring-cloud-context:jar:2.1.2.RELEASE:compile (version managed from 1.3.1.RELEASE)
[INFO] | \- org.springframework.security:spring-security-crypto:jar:5.1.5.RELEASE:compile
而不正常的情况下返回
[INFO] +- com.xiaohongshu:event-logging-spring-boot-autoconfigure:jar:0.0.7-SNAPSHOT:compile
[INFO] | \- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.6.RELEASE:compile
[INFO] | \- (org.springframework.boot:spring-boot:jar:2.1.6.RELEASE:compile - omitted for duplicate)
可实际上是被exlude掉的
<dependency>
<groupId>com.xxx</groupId>
<artifactId>event-logging-spring-boot-autoconfigure</artifactId>
<version>0.0.7-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>*</artifactId>
</exclusion>
显然在正常的代码里这部分没有被exluede掉。
可以找到对应的代码
[WARNING] 'dependencies.dependency.exclusions.exclusion.artifactId' for com.xxxx:event-logging-spring-boot-autoconfigure:jar with value '*' does not match a valid id pattern. @ line 282, column 33
实际上这是正常版本过低的问题
https://issues.apache.org/jira/browse/MNG-2315
所以,我们将我们的不正常版本的maven进行降级。就可以解决这个问题。