目录
Maven,['meɪv(ə)n],项目构建管理工具,用于告诉编译器项目中各文件之间的依赖关系等。
pom.xml 文件指定了项目的依赖关系。
1. 仓库
Maven的仓库分为本地仓库和远程仓库。
本地仓库:是Maven在我们本机设置的仓库目录,默认目录为 当前用户目录/.m2/repository。
C盘空间珍贵, 如何指定到别的磁盘呢? 添加下行配置即可.
<localRepository>D:\maven_local_repository</localRepository>
远程仓库:联网时才能用,从这里下载jar。
默认远程仓库因为访问量大,速度慢,所以可以自己临时替换远程仓库位置。
.m2/ 目录下有个settings.xml配置文件,在<mirrors>节点内部增加<mirror>节点就可以了。
一个示例:
<mirror>
<id>jboss-public-repository-group</id>
<mirrorOf>*</mirrorOf>
<name>JBoss Public Repository Group</name>
<url>http://repository.jboss.org/nexus/content/groups/public</url>
</mirror>
2. java项目结构
maven默认的文件存放结构如下:
/项目目录
pom.xml 用于maven的配置文件
/src 源代码目录
/src/main 工程源代码目录
/src/main/java 工程java源代码目录
/src/main/resources 工程的资源目录
/src/test 单元测试目录
/src/test/java
/target 输出目录,所有的输出物都存放在这个目录下
/target/classes 编译之后的class文件
SNAPSHOT:如果一个版本包含字符串"SNAPSHOT",Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC时间。例 如,"1.0-SNAPSHOT"会在2010年5月5日下午2点10分发布时候变成1.0-20100505-141000-1。
resources 资源文件的IO路径
图:打包后, resources/ 目录下的文件与 /src/main/java/ 平级
可使用 当前类.class.getClassLoader().getResourceAsStream("other/a.xlsx") 获取 InputStream. 注意区别 当前类.class.getResourceAsStream("/other/a.xlsx") 两种用法中的path中的最开始的斜杠的有无.
4.生命周期
compile、test、package、install、deploy。
4.1 compile
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
4.2 test
4.3 package
4.4 install
常见错误
[ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
解决方法:Library中的jre变成jdk,下图:
附带源码
可以使用maven-source-plugin插件。
4.5 deploy
用于发布到远程仓库。
5. pom.xml 常用标签
5.1 变量复用
<properties>标签.
<properties>
<spring.version>4.0.4</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>log4j-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
5.2 依赖相关
- 引入三方包依赖, 用 <dependency> 标签.
- 传递依赖的排除, 用 <exclusions> 标签.
- 指定依赖时机, 用 <scope> 标签
6. 依赖管理 与 冲突解决
6.1. 观察解析后的依赖
- `IDEA|Project视图|External Libraries`, 以平级形式展示的是实际引入的三方包.
- 也可用 mvn dependency:tree, 以层级形式展示依赖传递关系, 显式实际引入的三方包.
- `IDEA|Maven| ${project}|Dependencies`, 展示每个三方包自己的依赖树, 灰色表明冲突时被舍弃.
- 使用IDEA Maven Helper 插件. 打开pom文件, 子窗口底部会有 {Text, Dependency Analyzer}, 对冲突分析很有用.
Maven Helper 插件 功能截图
6.2. 为什么会遇到依赖冲突
约定符号: A->B:v1 表示 项目A依赖于项目B的v1版本.
依赖场景: 自己的项目P, P->A, P->B, A->C:v1, B->C:v2 , 见上图, 因形状得名 菱形依赖.
冲突原因: C的v1与v2有函数签名等的变化, 导致 实际运行时类找不到或语法校验错误.
能解决的情形: C的某个版本可以兼容, 想办法调整 pom 内容, 明确对该版本的依赖即可.
不能解决的情形: A用的方法只在 C:v1 中有, B用的方法只在 C:v2 中有, 则无解.
6.3. 依赖传递的仲裁规则
maven 面对冲突时, 有自己的默认规则:
- 最短路径优先
- 最先声明优先
- 版本低的优先
6.4. 实际case 笔记
6.4.1 明确指定C的版本
protobuf-java v2.5.0三方包中有个方法 , UnknownFieldSet com.google.protobuf.GeneratedMessage#getUnknownFields(),
swift_client 中有个类继承了该类,public static final class Messages extends GeneratedMessage {} , 然后重写了该方法, public final UnknownFieldSet com.alibaba.search.swift.protocol.SwiftMessage.Messages#getUnknownFields().
但在protobuf-javav2.4.1 中, getUnknownFields() 是 public final签名, 所以被omit之后, 导致报错.
解决办法: 按照最短路径优先, 直接新增一个 dependency, 指明版本为 2.5.0, 即可解决.
6.4.2 依靠 exclude 排除传递依赖
error-3sBZE>Caused by: com.alibaba.glaucus.client.domain.GlaucusRuntimeException: TppErrorCode-[SOLUTION_EXECUTE_ERROR]-[null], DETAIL_INFO: Host(11.227.244.142),solutionId(59278)
error-3sBZE>Caused by:
java.lang.LinkageError: loader constraint violation: when resolving method 'org.apache.http.impl.nio.client.HttpAsyncClientBuilder org.apache.http.impl.nio.client.HttpAsyncClientBuilder.setDefaultRequestConfig(org.apache.http.client.config.RequestConfig)'
the class loader com.alibaba.kondyle.loader.solution.loader.SolutionClassLoader @717caa80 of the current class, com/aliyun/openservices/eas/predict/http/PredictClient,
and
the class loader org.apache.catalina.loader.ParallelWebappClassLoader @66b5c7cc for the method's defining class, org/apache/http/impl/nio/client/HttpAsyncClientBuilder,
因为 httpcore 足够基础, 要在很多依赖中写 exclude.
6.4.3. 依靠scope 避免与运行容器的依赖冲突
如果冲突不来自于 传递依赖, 则可以靠 provided.
可以调整 <scope> 标签, 默认为 compile, 覆盖所有时机..
- provided: 编译时依赖, 打包分发时不依赖, 如 一些 servlet 容器.
- test: 测试时依赖, 如 junit.