Maven 核心概念与理论
Maven概念模型与依赖解析机制
Maven根据项目的pom.xml文件,把它转化成项目对象模型(POM),这时要解析依赖关系,然后去相对应的maven库中查找所依赖的jar包。在clean,compile,test,package
等生命周期阶段都有相应的Plug-in来做这些事情,而这些Plug-in会产生一些中间产物。
Maven从仓库解析依赖的机制
当本地仓库没有依赖构件的时候,Maven会自动从远程仓库下载;当依赖版本为快照版本时,Maven会自动找到最新的快照。
当依赖范围scope=system时,Maven直接从本地文件系统解析构件;
根据依赖坐标计算仓库路径后,尝试直接从本地仓库寻找构件,若发现构件则解析成功;
在本地仓库不存在相应构件的情况下,若依赖版本是显式的发布版本构件时,如1.1.0、1.2-alpha-1等,则便利所有的远程仓库,发现后下载到本地仓库并解析使用;
如果依赖的版本是RELEASE或者LASTEST,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml
,将其与本地仓库的对应元数据合并后,计算出RELEASE或者LASTEST的真实值,然后基于真实值检查本地和远程仓库;
如果依赖版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml
,将其与本地仓库的对应元数据合并后,得到最新快照版本的值,然后基于该值检查本地或者从远程仓库下载;
如果最后解析到的构件版本是时间戳格式的快照,如1.0-20170712.191220-2,则复制其时间戳格式的文件至非时间戳格式,如SNAPSHOT,并使用该非时间戳格式的构件。
当依赖的版本不明晰的时候,如RELEASE、LASTEST、SNAPSHOT,Maven就需要基于更新远程仓库的更新策略来检查更新。
Maven仓库
构件:在Maven的世界,任何一个依赖、插件或者项目构建的输出,即xxx.jar;任何一个构件都有一组坐标唯一标识。
仓库:得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的,在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。
public class DefaultRepositoryLayout
implements ArtifactRepositoryLayout
{ private static final char PATH_SEPARATOR = '/'; private static final char GROUP_SEPARATOR = '.'; private static final char ARTIFACT_SEPARATOR = '-'; public String getId()
{ return "default";
} public String pathOf( Artifact artifact )
{
ArtifactHandler artifactHandler = artifact.getArtifactHandler();
StringBuilder path = new StringBuilder( 128 );
path.append( formatAsDirectory( artifact.getGroupId() ) ).append( PATH_SEPARATOR );
path.append( artifact.getArtifactId() ).append( PATH_SEPARATOR );
path.append( artifact.getBaseVersion() ).append( PATH_SEPARATOR );
path.append( artifact.getArtifactId() ).append( ARTIFACT_SEPARATOR ).append( artifact.getVersion() ); if ( artifact.hasClassifier() )
{
path.append( ARTIFACT_SEPARATOR ).append( artifact.getClassifier() );
} if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )
{
path.append( GROUP_SEPARATOR ).append( artifactHandler.getExtension() );
} return path.toString();
} private String formatAsDirectory( String directory )
{ return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
}
}
例如:groupId=com.feiyue、artifactId=demo、version=1.0、artifactId=jdk7、packaging=jar
其对应的路径生成如下:
1). groupId路径:formatAsDirectory()
将groupId中的'.'转换成'/',com.feiyue
就会转换成com/feiyue
,之后再加一个'/',就变成com/feiyue/
2). artifactId路径:在groupId基础的加上artifactId,再加上一个'/,变成com/feiyue/demo/
3). version路径:在前面基础上加上version,再加上一个'/,变成com/feiyue/demo/1.0/
4). 依次加上artifactId、一个'-’、version,就变成com/feiyue/demo/1.0/demo-1.0
5). 如果有classfier,4)会变成com/feiyue/demo/1.0/demo-1.0-jdk7
6). 如果extension存在则依次加上'.’、extension。代码中extension是从artifactHandler而非artifact中获取,artifactHandler是由packaging决定的。故packaging决定了构件的扩展名,因此最终的路径为com/feiyue/demo/1.0/demo-1.0-jdk7.jar
Maven仓库的分类
Maven仓库分为两类:本地仓库和远程仓库。当Maven根据坐标寻找构件时,首先会查看本地仓库,若本地仓库存在此构件则直接使用;若本地仓库不存在此构件,Maven就会去远程仓库查找,查找到下载到本地仓库再使用。若本地仓库和远程仓库都没有需要的构件,Maven就会报错。
中央仓库: Maven核心自带的远程仓库,包含了绝大部分开源构件,默认情况,当本地仓库没有Maven需要构件时,就从中央仓库下载。
私服:一种特殊的远程仓库,为节省带宽和时间,应在局域网内架设一个私有仓库服务器,用其代理所有外部的远程仓库。
本地仓库:用户自定义本地仓库的地址,需编辑${user.home}/.m2/setting.xml
文件,设置localRepository节点的值为仓库地址即可,默认情况下${user.home}/.m2/setting.xml
是不存在的,需要用户从安装目录复制${M2_HOME}/conf/setting.xml
文件在进行编辑。
<settings><localRepository>E:/repository</localRepository></settings>
中央仓库: Maven默认的远程仓库,安装文件中自带了中央仓库的配置,在${M2_HOME}/lib/maven-model-builder-3.2.5.jar
中,解压缩找到org\apache\maven\model\pom-4.0.0.xml
,可以看到如下默认远程仓库配置:
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository></repositories>
这个配置文件是所有Maven项目都会继承的超级POM.
私服:特殊的远程仓库,架设在局域网内的仓库服务,代理公网的远程仓库,当Maven需要下载构件时,从私服请求,若私服不存在该构件,则从公网远程仓库下载,缓存到私服之后,再为Maven的下载请求提供服务。另外无法从公网仓库下载的构件也能从本地上传到私服供项目使用。
私服优点:节省外网带宽、提供Maven构件速度、部署第三方构件、提供Maven构件稳定性、降低中央仓库负荷。
Maven坐标
唯一标识Maven构件,坐标元素分为groupId、artifactId、version、packaging、classifier
.groupId:
必选,定义当前Maven项目隶属的实际项目,不一定是一对一的关系,通常一个实际项目会被划分成很多模块。groupId一般不应该只定义到公司级别,一个公司可能会有很多实际项目,如果groupId只定义到组织级别,那么artifactId只能定义Maven项目。命名方式和Java包名类似,域名反向一一对应。例如:org.springframework.artifactId:
必选,定义实际项目中的一个Maven模块,推荐使用实际项目名称-模块名称,这样便于找到某个项目的一组构件。例如:spring-core,spring-beans,spring-web等。version:
必选,定义Maven项目当前所处的版本。例如:4.3.9.RELEASE、1.0-SNAPSHOT、RELEASE、LATEST、2.1等。packaging:
可选默认是jar,定义Maven项目的打包方式。打包方式有jar、war、pom等。classifier:
不能直接定义,帮助定义构建输出的一些附属构件。附属构件与主构件对应,例如-javadoc.jar、-sources.jar附属构件包含了java文档和源代码。
依赖管理
依赖管理分为传递性依赖、依赖调解、可选依赖、排除依赖、归类依赖等。
-----------转载自微信公众号:云时代架构