Apache Maven Compat 兼容层解析:Maven 2 遗留类的作用、源码结构与插件迁移指南
2026/9/17 15:32:59 网站建设 项目流程

Apache Maven Compat 兼容层解析:Maven 2 遗留类的作用、源码结构与插件迁移指南

【免费下载链接】mavenApache Maven core项目地址: https://gitcode.com/GitHub_Trending/ma/maven

Apache Maven 4 核心仓库(本项目)在compat/maven-compat模块中维护了一批 Maven 2 时代的类,作为兼容层供那些仍需保持 Maven 2 API 兼容性的插件使用。本文以 compat/maven-compat/src/site/markdown/index.md 为骨架,结合该模块的 pom.xml、src/main/java源码与src/test/java测试,系统讲解这个兼容层是什么、里面有什么、底层如何工作,以及插件开发者应当如何对待它、如何迁移到纯 Maven 3 依赖。

读完本文,你将掌握 maven-compat 模块的完整边界(哪些包、哪些类属于遗留 API),理解它如何用新架构(Aether、Sisu DI)重新实现 Maven 2 语义,并得到一份可落地的插件迁移检查清单。

Maven Compat 是什么:一句话定位

根据模块官网介绍页的原文定义:

Maven2 classes maintained as compatibility layer for plugins that need to keep Maven2 compatibility.

即:为需要保持 Maven 2 兼容的插件而维护的 Maven 2 类兼容层。它的存在意义不是长期 API,而是过渡性的"缓冲垫"——让老插件在升级到 Maven 3/4 运行时后仍能编译和运行,同时给插件作者留出迁移窗口。

这一点在 compat/maven-compat/pom.xml 中被直接写进了构件元数据:

  • <name>Maven 2 Compat (deprecated)</name>
  • <description>Deprecated Maven 2 classes, maintained as compatibility layer.</description>

构件名(artifactId)为maven-compat,版本与整个 Maven 4.1.0-SNAPSHOT 主干保持一致,属于org.apache.maven下的maven-compat-modules父模块。名字里 "deprecated" 一词已经表明官方态度:这套类是历史遗留,新代码不应使用

为什么需要兼容层:Maven 2 API 的历史包袱

Maven 2 时代,插件依赖的是以org.apache.maven.artifact.*org.apache.maven.project.MavenProjectBuilderorg.apache.maven.profiles.ProfileManager为代表的一套组件 API。Maven 3 引入了 Eclipse Aether(依赖解析)和 Sisu(依赖注入),底层架构发生了根本性变化,但大量存量插件仍然直接引用这些 Maven 2 接口。

如果 Maven 3/4 直接删除这些类,所有未升级的插件都会在运行时因ClassNotFoundException/NoSuchMethodError崩溃。maven-compat 模块正是为此存在的:它把这些 Maven 2 类继续打包进 Maven 运行时,并用新架构重新实现其行为,从而保证老插件的二进制兼容。

从源码可以清楚看到这一点——该模块几乎所有顶层类都带@Deprecated注解。例如:

  • LegacyRepositorySystem.java:@Named("default") @Singleton @Deprecated
  • DefaultLegacyArtifactCollector.java:@Named @Singleton @Deprecated
  • DefaultMavenProjectBuilder.java:@Deprecated并实现MavenProjectBuilder

可以说,"兼容层 = 全部打上过时标记的 Maven 2 组件集合"是阅读这个模块时最重要的心智模型。

模块依赖:兼容层骑在新架构之上

阅读 compat/maven-compat/pom.xml 的<dependencies>段,能直观看到这个"旧 API、新实现"的混合形态:

面向新架构的依赖(实现底座):

  • maven-api-coremaven-api-dimaven-api-modelmaven-api-annotationsmaven-api-metadatamaven-api-toolchain(api 系列,位于仓库 api 目录)
  • maven-impl(位于 impl/maven-impl)、maven-core(位于 impl/maven-core)
  • maven-resolver-api/maven-resolver-util/maven-resolver-impl(Eclipse Aether 解析器三件套)

兼容层自身维护的 Maven 2 门面(依赖 compat 目录内的其他模块):

  • maven-artifactmaven-modelmaven-model-buildermaven-settingsmaven-settings-buildermaven-plugin-apimaven-repository-metadatamaven-toolchain-buildermaven-toolchain-modelmaven-resolver-provider

运行时兼容所需的历史依赖(pom 注释写得很直白):

  • javax.inject:注释为"only for backward compatibility otherwhise would be provided"
  • aopalliance:1.0:同样仅为向后兼容
  • org.eclipse.sisu.injectorg.eclipse.sisu.plexus:同上
  • com.google.inject:guice(classifierclasses):注释为"only for backward compatibility otherwhise would be test"
  • org.codehaus.plexus:plexus-component-annotations:2.1.0:注释为"ONLY version here; nothing else use or should use this dependency"

这些注释本身就是重要的工程信息:这些依赖纯粹为了让旧插件能工作而保留,新代码不应该触碰它们

构建配置方面,pom 中启用了sisu-maven-plugin(生成 Sisu 组件索引,让兼容层里的@Named组件可被容器发现),以及modello-maven-plugin,从 src/main/mdo/profiles.mdo 和 src/main/mdo/paramdoc.mdo 两个模型定义生成 Java 代码与 xpp3 读写器——这解释了测试目录里为何有profiles相关测试。

兼容层包含哪些 Maven 2 遗留组件

src/main/java/org/apache/maven下的包结构,可以把兼容层划分成几大功能域(对应真实目录 compat/maven-compat/src/main/java/org/apache/maven):

1. 构件与仓库(artifact 包族)

  • artifact.deployerArtifactDeployer/DefaultArtifactDeployer,负责把构件部署到远程仓库
  • artifact.installerArtifactInstaller/DefaultArtifactInstaller,负责安装构件到本地仓库
  • artifact.managerWagonManager/DefaultWagonManager,Wagon 传输的旧式管理器
  • artifact.metadataArtifactMetadataSourceResolutionGroupAbstractArtifactMetadata等,旧式元数据源
  • artifact.repositoryArtifactRepositoryFactoryDefaultArtifactRepositoryLegacyLocalRepositoryManagerFlatRepositoryLayout等,旧式仓库抽象
  • artifact.resolver:构件解析与过滤器(ArtifactResolutionResult、各种ArtifactFilter等)
  • artifact.versioningVersionRangeArtifactVersion相关版本处理
  • 顶层还保留ArtifactScopeEnumArtifactStatusUnknownRepositoryLayoutException

2. 仓库兼容实现(repository 包族)

  • repository.legacyLegacyRepositorySystem(兼容层的核心门面,见下文)、DefaultWagonManagerDefaultUpdateCheckManagerChecksumFailedExceptionMavenArtifactTransferListenerAdapter
  • repository.legacy.repositoryArtifactRepositoryFactory/DefaultArtifactRepositoryFactory
  • repository.legacy.resolverLegacyArtifactCollector/DefaultLegacyArtifactCollector,以及 4 个旧式冲突解决器NearestConflictResolverFarthestConflictResolverNewestConflictResolverOldestConflictResolver;还有版本转换器LatestArtifactTransformationReleaseArtifactTransformationSnapshotTransformation
  • repository.legacy.metadata:旧式元数据解析
  • repository.metadataArtifactMetadataRetrievalException
  • repository顶层:MirrorSelector/DefaultMirrorSelector、传输事件/监听器(ArtifactTransferEventArtifactTransferListenerArtifactTransferResource

3. 项目模型构建(project 包族)

  • projectMavenProjectBuilder/DefaultMavenProjectBuilderProjectBuilderConfigurationModelUtilsProjectUtilsInvalidProjectModelException
  • project.artifactMavenMetadataSource
  • project.inheritance:旧式继承合并
  • project.interpolation:旧式插值器(StringSearchModelInterpolator等)
  • project.pathproject.validation:路径转换与模型校验

4. Profile 管理(profiles 包族)

  • ProfileManager/DefaultProfileManager/ProfilesConversionUtils以及profiles.activation下的 7 个激活器,负责 Maven 2 风格的 profile 激活逻辑

5. 其他杂项

  • plugin.PluginManager:旧式插件管理器门面
  • toolchain:16 个文件,旧式工具链 API
  • reporting.MavenReportException:报告插件异常类型
  • settingsexecutionRuntimeInformation/DefaultRuntimeInformation)、usabilityDefaultMavenProjectBuilder相关提示)
  • 模块顶层:ArtifactFilterManagerProjectDependenciesResolver及其默认实现,它们均标有@Deprecated

从上面的清单可以看出,兼容层几乎覆盖了 Maven 2 时代插件会接触的全部 API 面:依赖解析、构件安装/部署、仓库镜像、元数据、版本范围、profile 激活、项目构建、工具链、报告。这也是它体积较大的原因。

源码级原理:旧 API 如何跑在新引擎上

LegacyRepositorySystem:仓库系统的 Maven 2 门面

LegacyRepositorySystem.java(共 839 行)实现了org.apache.maven.repository.RepositorySystem接口,但内部几乎全部委托给注入的旧式组件:

  • createArtifact(...)系列方法委托给ArtifactFactory(如createArtifactWithClassifiercreateProjectArtifact);
  • createDependencyArtifact(Dependency d)先把版本字符串解析为VersionRange,再委托artifactFactory.createDependencyArtifact(...);对system作用域且带systemPath的依赖会设置本地文件,并把依赖的<exclusions>转成ExcludesArtifactFilter过滤器;
  • createPluginArtifact(Plugin plugin)在插件未声明版本时默认补成"RELEASE"
  • buildArtifactRepositoryPolicy(RepositoryPolicy policy)把新模型中的<repository><releases/></repository>策略(enabledupdatePolicychecksumPolicy)转成旧式ArtifactRepositoryPolicy三元组;
  • createLocalRepository(File)构造file://URL 交给旧式仓库工厂。

值得注意的是其中的异常处理(MNG-5368):当依赖或插件声明了非法版本规格时,不再静默返回null,而是通过Logger.error输出Invalid version specification ...日志后返回null——这是兼容层在多年演进中修补过的行为细节,也说明这些遗留代码并非冻结不变,仍在做防御性维护。

DefaultLegacyArtifactCollector:旧式依赖收集

DefaultLegacyArtifactCollector.java 实现旧式LegacyArtifactCollector,负责把一组构件连同版本管理信息、本地/远程仓库、元数据源、过滤器、监听器收集成ArtifactResolutionResult

它有两个值得关注的细节:

  1. 默认冲突策略是 "nearest":通过@Inject @Named("nearest") private ConflictResolver defaultConflictResolver;注入,即 Maven 2 时代经典的"最近依赖优先"语义。conflict包下还提供了FarthestNewestOldest三种可选策略,由ConflictResolverFactory按需创建。
  2. 运行时上下文注入injectSession(ArtifactResolutionRequest request)LegacySupport.getSession()取出当前MavenSession,把离线标志(offline)、是否强制更新快照(forceUpdate)、settings 中的 servers/mirrors/proxies 灌入解析请求。这意味着旧式收集器在执行时依然遵循用户在命令行和 settings.xml 中配置的全局行为,而不是孤立地解析。

版本转换与元数据:snapshot/latest/release 语义

repository.legacy.resolver.transform包下的SnapshotTransformationLatestArtifactTransformationReleaseArtifactTransformation负责把SNAPSHOTLATESTRELEASE这类元版本解析为具体版本,由ArtifactTransformationManager统一调度——这正是 Maven 2 时代maven-metadata.xml使用方式的一部分,对应测试目录中的artifact/transform/TransformationManagerTest

本地仓库元数据与安全性

artifact.repository下的LegacyLocalRepositoryManager负责旧式本地仓库的元数据文件命名,例如把仓库central的元数据映射为maven-metadata-central.xml。对应的测试 LegacyLocalRepositoryManagerTest.java 验证了三条规则:

  • 格式良好的仓库 id(如central)生成的本地文件名保持不变;
  • 仓库 id 含路径分隔符(如repo/evil)时抛出IllegalArgumentException,防止路径穿越;
  • 仓库 id 是父目录引用(如..)时同样拒绝。

这说明即使是"历史兼容"代码,也包含了针对仓库 id 注入攻击的安全防护,属于可被引用的实现事实。

测试体系:兼容层不是"死代码"

模块在 compat/maven-compat/src/test/java/org/apache/maven 下保留了约 86 个测试文件,覆盖各功能域:

  • 解析:artifact/resolver/DefaultArtifactResolverTestArtifactResolverTest、过滤器测试(AndArtifactFilterTestOrArtifactFilterTestScopeArtifactFilterTestFilterHashEqualsTest
  • 安装/部署:artifact/installer/ArtifactInstallerTestartifact/deployer/ArtifactDeployerTest
  • 元数据:artifact/repository/metadata/DefaultRepositoryMetadataManagerTest...ValidationTest
  • 项目继承:project/inheritance/下 t00~t12+ 系列ProjectInheritanceTest,用多组 POM 样本验证继承合并语义
  • 插值:project/interpolation/StringSearchModelInterpolatorTest
  • Profile:profiles/manager/DefaultProfileManagerTest
  • 顶层:ProjectDependenciesResolverTest(配套测试项目位于 compat/maven-compat/src/test/projects/project-dependencies-resolver,其中project-with-exclusions用于验证排除依赖场景),以及公共基类AbstractCoreMavenComponentTestCase/AbstractArtifactComponentTestCase/AbstractMavenProjectTestCase

此外src/test/remote-repo提供了模拟远程仓库的构件,src/test/repository-system/maven-core-2.1.0.jar这类测试资源则直接用于验证对真实 Maven 2 时代构件的兼容行为。

插件开发者行动指南:为什么应当迁移

文档原文明确表达了立场:

Plugins should avoid these classes and be updated to use only Maven3 dependencies (and require Maven3).

即:插件应当避免使用这些类,并更新为只依赖 Maven 3 依赖(并要求 Maven 3 作为运行前提)。官方 Wiki 上有专门的"Plugin migration to Maven3 dependencies"迁移指南提供具体改法;结合本仓库源码,迁移要点可以归纳为:

  1. 识别命中面:检查插件源码的 import,凡指向org.apache.maven.artifact.*org.apache.maven.project.MavenProjectBuilderorg.apache.maven.profiles.ProfileManagerorg.apache.maven.plugin.PluginManagerorg.apache.maven.toolchain.*(旧式)等包名的,都属于本模块维护的遗留 API。
  2. 替换依赖解析 API:用org.eclipse.aether.*(解析器)与maven-api-core中的新接口替代旧式ArtifactResolver/LegacyArtifactCollector/VersionRange组合。旧式"最近优先"冲突策略在新解析器中由NearestVersionSelector等策略组件承担,语义可以平滑过渡。
  3. 替换项目构建 API:用新模型构建器(maven-model-buildermaven-api-model)替代MavenProjectBuilder;用maven-api-di的标准注解进行组件注入,而不是依赖兼容层里的 Plexus/Sisu 旧注解路径。
  4. 声明 Maven 3 最低版本:在插件 POM 的<prerequisites>中声明对 Maven 3 的要求,使构建期即可拦截不兼容环境,而不是在运行时才暴露问题。
  5. 回归验证:迁移后运行原有集成场景(本仓库its/core-it-suite中大量基于插件 API 的集成测试即是这类验证的参考),重点覆盖依赖解析顺序、snapshot 更新、profile 激活、排除依赖等行为是否与迁移前一致。

总结

maven-compat 是 Apache Maven 核心仓库中一个"被官方标记为废弃却仍在认真维护"的过渡模块:

  • 定位:为需要保持 Maven 2 兼容性的插件提供类兼容层(见 compat/maven-compat/src/site/markdown/index.md);
  • 规模:覆盖构件解析/安装/部署/元数据/仓库/版本、项目构建、profile 激活、工具链等 Maven 2 全部插件 API 面,几乎所有类都带@Deprecated注解;
  • 实现:依赖清单与源码共同证明它是"旧接口 + 新引擎"——底层由 api/impl 系列模块与 Eclipse Aether 支撑,LegacyRepositorySystemDefaultLegacyArtifactCollector等类负责把 Maven 2 语义翻译到新架构上;
  • 态度:官方明确要求插件尽快迁移到纯 Maven 3 依赖,兼容层只是过渡桥梁,不是新代码的目标 API。

对于维护存量插件的开发者,本模块既是"为什么不能直接删掉 Maven 2 API"的答案,也是"迁移后应该长什么样"的反面参照物;对于想理解 Maven 内部演进史的读者,它则是一份浓缩的架构变迁档案。

【免费下载链接】mavenApache Maven core项目地址: https://gitcode.com/GitHub_Trending/ma/maven

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询