当前位置: 首页 > news >正文

maven:Maven插件开发实践:动态依赖注入与架构演进说明

最近的工作中设计了一个maven插件,需要在插件执行时的增加新的依赖库,本文作为总结,说明如何在插件执行时自主注入新的依赖库。

动态依赖注入实现

示例解析

通过ExampleMojo插件,我们可以在编译阶段动态注入指定的依赖:

public void execute() throws MojoExecutionException {
    ArtifactSupport.injectCacheEngine(
        project, repositorySystem, 
        repoSession, remoteRepos, getLog()
    );
    // 执行核心业务逻辑
    processAspectWeaving();
}

核心注入逻辑通过Aether实现依赖解析:

// 构建Aether构件坐标
Artifact aetherArtifact = new DefaultArtifact(
    "com.example", 
    "cache-engine", 
    "jar", 
    "2.1.0"
);

// 解析远程仓库中的构件
ArtifactResult result = repoSystem.resolveArtifact(
    repoSession,
    new ArtifactRequest(aetherArtifact, remoteRepos)
);

// 转换为Maven原生Artifact
DefaultArtifact mavenArtifact = new DefaultArtifact(
    result.getArtifact().getGroupId(),
    result.getArtifact().getArtifactId(),
    result.getArtifact().getVersion(),
    "provided",
    result.getArtifact().getExtension(),
    result.getArtifact().getClassifier(),
    new DefaultArtifactHandler("jar")
);

// 绑定物理文件路径
mavenArtifact.setFile(result.getArtifact().getFile());

// 注入项目构建流程
project.getArtifacts().add(mavenArtifact);

为什么直接修改dependencies无效?

开始看到MaveProject有dependencies字段,就想当然的认为只要将依赖添加到dependencies列表中就可以了,事实上没有效果,原因如下:

  • 生命周期限制:依赖解析在initialize阶段完成,后续修改不会触发重新解析
  • 作用域隔离project.dependencies管理声明式依赖,project.artifacts存储已解析结果
  • 缓存机制:Maven会缓存依赖树,运行时修改无法影响已缓存的元数据

架构演进背后的思考

做这个插件时,开始参考了很多网上的示例,多数有些过时,使用了一些废弃的注解和对象,导致我走了弯路,在此一并总结。

从@Component到JSR 330

@Component注解废弃了,旧版注入方式:

@Component
private ArtifactFactory factory;

现代注入规范:

@Inject
public ExampleMojo(RepositorySystem system) {
    this.repositorySystem = system;
}
/** 也可以直接将注解写在字段上,但字段不可定义为final */
@Inject
private RepositorySystem repositorySystem;

演进原因:

  • 标准化:遵循Java依赖注入标准(JSR 330)
  • 兼容性:支持多种DI容器(Guice、Spring等)
  • 可测试性:构造函数注入更易于单元测试
  • 生命周期:明确组件初始化顺序

演进优势对比

特性旧方案(@Component)新方案(@Inject)
标准化Maven专属Java EE标准
可测试性需要模拟Plexus容器支持任意DI容器
生命周期管理隐式初始化显式构造函数控制
兼容性仅限Maven 2.x支持Maven 3.x+

Aether的崛起

ArtifactFactory也废弃了,这又是一个坑,根据 Maven 3.0+ API 规范,使用 纯 Aether API 实现 Artifact 管理,完全替代废弃的 ArtifactFactory

Maven依赖管理演进路线:

Maven 2.x (原生) → Maven 3.x (Aether) → Maven Resolver (最新)

优势对比:

特性原生实现Aether
解析速度100ms/req50ms/req
并发支持
扩展性有限插件化架构
依赖树算法简单DFS高级冲突解决
远程仓库协议HTTP/FTP支持S3等

完整实现代码

ArtifactSupport.java(核心工具类)

import java.util.List;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;

public class ArtifactSupport{
    /** 切面库模块名称 */
    private static final String AOCACHE_AJ = "aocache-aj";
	/** aocache 运行时依赖名称 */
    private static final String AOCACHE = "aocache";
	/** aocache 组名 */
    private static final String AOCACHE_GROUP_ID = "com.gitee.l0km";
    
    static void addAocacheAjArtifact(
		    MavenProject project, 
		    RepositorySystem repoSystem,
		    RepositorySystemSession repoSession,
		    List<RemoteRepository> remoteRepos,
		    Log log
		) throws MojoExecutionException {
		    
		    // 1. 检查运行时依赖是否存在
		    Dependency runtimeDep = project.getDependencies().stream()
		        .filter(d -> AOCACHE_GROUP_ID.equals(d.getGroupId()) 
		            && AOCACHE.equals(d.getArtifactId()))
		        .findFirst()
		        .orElseThrow(() -> new MojoExecutionException("缺少aocache运行时依赖"));

		    // 2. 构建aocache-aj构件坐标
		    String ajVersion = runtimeDep.getVersion();
		    Artifact ajArtifact = new DefaultArtifact(
		        AOCACHE_GROUP_ID, 
		        AOCACHE_AJ, 
		        "jar", 
		        ajVersion
		    );
		    // 3. 检查是否已存在该构件
		    boolean exists = project.getArtifacts().stream()
		            .anyMatch(a -> 
		                AOCACHE_GROUP_ID.equals(a.getGroupId()) &&
		                AOCACHE_AJ.equals(a.getArtifactId()) &&
		                ajVersion.equals(a.getVersion()) &&
		                "jar".equals(a.getType()) &&
		                (a.getClassifier() == null || a.getClassifier().isEmpty())
		            );
		    
		    if (exists) {
		        log.debug("aocache-aj已存在于项目artifacts");
		        return;
		    }

		    // 4. 解析构件
		    try {
		        ArtifactResult result = repoSystem.resolveArtifact(
		            repoSession,
		            new ArtifactRequest(ajArtifact, remoteRepos, null)
		        );
		        
		        // 5. 转换为Maven原生Artifact并注入
		        org.apache.maven.artifact.Artifact mavenArtifact = 
		        		new org.apache.maven.artifact.DefaultArtifact( // 使用maven-core内置实现
		                        result.getArtifact().getGroupId(),
		                        result.getArtifact().getArtifactId(),
		                        result.getArtifact().getVersion(),
		                        "provided",
		                        result.getArtifact().getExtension(),
		                        result.getArtifact().getClassifier(),
		                        null
		                    );
		        mavenArtifact.setFile(result.getArtifact().getFile());
		        project.getArtifacts().add(mavenArtifact);
//		        project.addAttachedArtifact(mavenArtifact);
		        log.info("成功注入aocache-aj构件: " + ArtifactUtils.key(mavenArtifact));
		        
		    } catch (ArtifactResolutionException e) {
		        throw new MojoExecutionException("解析aocache-aj失败: " + ajArtifact, e);
		    }
		}
}

ExampleMojo.java(插件入口)

// 导入包略

@Mojo( name="example", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true )
public class ExampleMojo extends AbstractMojo {
  
    @Inject
    private RepositorySystem repositorySystem;
  
    @Parameter(defaultValue = "${repositorySystemSession}")
    private RepositorySystemSession repoSession;
  
    @Parameter(defaultValue = "${project.remoteProjectRepositories}")
    private List<RemoteRepository> remoteRepos;

    @Override
    public void execute() {
        // 依赖注入与业务逻辑分离
        ArtifactSupport.injectCacheEngine(project, repoSystem, ...);
        processBusinessLogic();
    }
  
    private void processBusinessLogic() {
        // 核心业务实现...
    }
}

参考资料

  1. Maven官方文档 - 插件开发指南
  2. Eclipse Aether用户手册
  3. JSR 330标准规范

相关文章:

  • 快速排序(详解)c++
  • 【Java分布式】基本概念
  • 《模拟器过检测教程:Nox、雷电、Mumu、逍遥模拟器 Magisk、LSposed 框架安装与隐藏应用配置》
  • 模拟与数字电路 寇戈 第三节课后习题
  • 【计网】计算机网络概述
  • 记一次内存泄漏导致的线上超时问题
  • JMH 详细使用
  • 超过DeepSeek、o3,Claude发布全球首个混合推理模型,并将完成新一轮35亿美元融资...
  • 探索AI新前沿,CoT推理赋能文生图!港中文首次提出文生图的o1推理和inference scaling新范式
  • 【嵌入式原理设计】实验五:远程控制翻盖设计
  • 1998-2022年各地级市第一产业占GDP比重数据/(全市)
  • 某住宅小区地下车库安科瑞的新能源汽车充电桩的配电设计与应用方案
  • 从实测看声网:用技术重构直播,为电商创业赋能
  • PhotoShop学习01
  • 自定义mybatis拦截器,在springboot项目中不起作用的解决方法
  • 计算机毕业设计SpringBoot+Vue.js电影评论网站系统(源码+文档+PPT+讲解)
  • python学习四
  • 玻璃非球面加工:倚光科技助力高精度光学元件制造
  • 跟着AI学vue第十二章
  • IIC基础知识(集成电路总线)
  • 联合国第二届运动会闭幕,刘国梁受邀成为“联合国运动会大使”
  • 蚊媒传染病、手足口病……上海疾控发布近期防病提示
  • 国台办:台湾自古属于中国,历史经纬清晰,法理事实清楚
  • 上海145家博物馆、73家美术馆将减免费开放
  • 第十届影像上海博览会落幕后,留给中国摄影收藏的三个问题
  • 王毅人民日报撰文:共商发展振兴,共建中拉命运共同体