Maven:插件解析机制。

为了方便用户使用和配置插件,Maven不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件,Maven的这一特性是一把双刃剑,虽然他简化了插件的使用和配置,可一旦插件的行为出现异常,用户就很难快速定位到问题的插件构件。例如mvn help:system这样一条命令,他到底执行了什么插件?该插件的groupId、artifactId和version分别是什么?这个构件是从哪里来的?本文就详细介绍Maven的运行机制,以让其不仅知其然,更知其所以然。

插件仓库

与依赖构件一样,插件构件同样基于坐标存储在Maven仓库中。在需要的时候,Maven会从本地仓库寻找插件,如果不存在,则从远程仓库查找。找到插件之后,再下载到本地仓库使用。

值得一提的是,Maven会区别对待依赖的远程仓库与插件的远程仓库。当Maven需要的依赖在本地仓库不存在时,他会去所配置的远程仓库查找,可是当Maven需要的插件在本地仓库不存在时,他就不会去这些远程仓库查找。

不同于repositories及其repository子元素,插件的远程仓库使用pluginRepositories和pluginRepository配置。例如,Maven内置了如下的插件远程仓库配置,见下面所示。

<pluginRepositories>
    <pluginRepository>
        <id>central</id>
        <name>Maven Plugin Repository</name>
        <url>http://repo1.maven.org/maven2</url>
        <layout>default</layout>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <releases>
            <updatePolicy>never</updatePolicy>
        </releases>
    </pluginRepository>
</pluginRepositories>

除了pluginRepositories和pluginRepository标签不同之外,其余所有子元素表达含义与依赖远程仓库配置完全一样。我们甚至看到,这个默认插件仓库的地址就是中央仓库,他关闭了对SNAPSHOT的支持,以防止引入SNAPSHOT版本的插件而导致不稳定的构建。

一般来说,中央仓库所包含的插件完全能够满足我们的需要,因此也不需要配置其他的插件仓库。只有在很少的情况下,项目使用的插件无法在中央仓库找到,或者自己编写了插件,这个时候可以参考上述的配置,在POM或者settings.xml中加入其他的插件仓库配置。

插件的默认groupId

在POM中配置插件的时候,如果该插件是Maven的官方插件(即如果其groupId为org.apache.maven.plugins),就可以省略groupId配置,见下面所示。

<build>
    <plugins>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.1</version>
        <configuration>
            <source>1.5</source>
            <target>1.5</target>
        </configuration>
    </plugins>
</build>

上述配置中省略了maven-compiler-plugin的groupId,Maven在解析该插件的时候,会自动用默认groupId org.apache.maven.plugins补齐。

不推荐使用Maven的这一机制,虽然这么做可以省略一些配置,但这样的配置会让团队中不熟悉Maven的成员感到费解,况且能省略的配置也就仅仅一行而已。

解析插件版本

同样是为了简化插件的配置和使用,在用户没有提供插件版本的情况下,Maven会自动解析插件版本。

首先,Maven在超级POM中为所有核心插件设定了版本,超级POM是所有Maven项目的父POM,所有项目都继承这个超级POM的配置,因此,即使用户不加任何配置,Maven使用核心插件的时候,他们的版本就已经确定了。这些插件包括maven-clean-plugin、maven-compiler-plugin、maven-surefire-plugin等。

如果用户使用某个插件时没有设定版本,而这个插件又不属于核心插件的范畴,Maven就会去检查所有仓库中可用的版本,然后做出选择。以maven-compiler-plugin为例,他在中央仓库的仓库元数据为http://repol.maven.org/maven2/org/apache/maven/plugins/maven-compiler/maven-metadata.xml,其内容如下所示。

<?xml version="1.0" encoding="UTF-8" ?>
<metadata>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <versioning>
        <latest>2.1</latest>
        <release>2.1</release>
        <versions>
            <version>2.0-beta-1</version>
            <version>2.0</version>
            <version>2.0.1</version>
            <version>2.0.2</version>
            <version>2.1</version>
        </versions>
    </versioning>
</metadata>

Maven遍历本地仓库和所有远程插件仓库,将该路径下的仓库元数据归并后,就能计算出latest和release的值。latest表示所有仓库中该构件的最新版本,而release表示最新的非快照版本。在Maven 2中,插件的版本会被解析至latest。也就是说,当用户使用某个非核心插件且没有声明版本的时候,Maven会将版本解析为所有可用仓库中的最新版本,而这个版本也可能是快照版。

当插件的版本为快照版本时,就会出现潜在的问题。Maven会基于更新策略,检查并使用快照的更新。某个插件可能昨天还用的好好的,今天就出错了,其原因就是这个快照版本的插件发生了变化。为了防止这类问题,Maven 3调整了解析机制,当插件没有声明版本的时候,不再解析至latest,而是使用release。这样就可以避免由于快照频繁更新而导致的插件行为不稳定。

依赖Maven解析插件版本其实是不推荐的做法,即使Maven 3 将版本解析到最新的非快照版,也还是会有潜在的不稳定性。例如,可能某个插件发布了一个新的版本,而这个版本的行为与之前的版本发生了变化,这种变化就可能导致项目构件失败。因此,使用插件的时候,应该一直显式的设定版本,这也解释了Maven为什么要在超级POM中为核心插件设定版本。

解析插件前缀

现在解释Maven如何根据插件前缀解析得到插件坐标。

插件前缀与groupId:artifactId是一一对应的,这种匹配关系存储在仓库元数据中。与groupId/artifactId/maven-metadata.xml不同,这里的仓库元数据为groupId/maven-metadata.xml,那么这里的groupId是什么呢?主要的插件都位于http://repol.maven.org/maven2/org/apache/maven/plugins和http://repository.codehaus.org/org/codehaus/mojo/,相应地,Maven在解析插件仓库元数据的时候,会默认使用org.apache.maven.plugins和org.codehaus.mojo两个groupId。也可以通过配置settings.xml让Maven检查其他groupId上的插件仓库元数据:

<settings>
    <pluginGroups>
        <pluginGroup>com.your.plugins</pluginGroup>
    </pluginGroups>
</settings>

基于该配置,Maven就不仅仅会检查org/apache/maven/plugins/maven-metadata.xml和org/codehaus/mojo/maven-metadata.xml,还会检查com/your/plugins/maven-metadata.xml。

下面看一下插件仓库元数据的内容,见下面。

<metadata>
    <plugins>
        <plugin>
            <name>Maven Clean Plugin</name>
            <prefix>clean</prefix>
            <artifactId>maven-clean-plugin</artifactId>
        </plugin>
        <plugin>
            <name>Maven Compiler Plugin</name>
            <prefix>compiler</prefix>
            <artifactId>maven-compiler-plugin</artifactId>
        </plugin>
        <plugin>
            <name>Maven Dependency Plugin</name>
            <prefix>dependency</prefix>
            <artifactId>maven-dependency-plugin</artifactId>
        </plugin>
    </plugins>
</metadata>

上述内容是从中央仓库的org.apache.maven.plugins.groupId下插件仓库元数据中截取的一些片段,从这段数据中就能看到maven-clean-plugin的前缀为clean,maven-compiler-plugin的前缀为compiler,maven-dependency-plugin的前缀为dependency。

当Maven解析到dependency:tree这样的命令后,他首先基于默认的groupId归并所有插件仓库的元数据org/apache/plugins/maven-metadata.xml;其次检查归并后的元数据,找到对应的artifactId为maven-dependency-plugin;然后结合当前元数据的groupId为org.apache.maven.plugins;最后解析得到version,这时就得到了完整的插件坐标。如果org/apache/maven/plugins/maven-metadata.xml,以及用户自定义的插件组。如果所有元数据中都不包含该前缀,则报错。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值