标签 依赖注入 下的文章

写在前面,本人目前处于求职中,如有合适内推岗位,请加:lpshiyue 感谢。

优秀的架构不是一次性的设计杰作,而是通过持续评审、债务治理和渐进式重构形成的有机体系

在构建了高可用的容灾体系后,我们面临一个更根本的挑战:如何确保系统架构本身具备持续演进的能力?架构评审与技术债治理正是连接短期交付压力与长期架构可持续性的关键桥梁。本文将深入探讨架构质量属性、演进式重构方法论与风险评估框架,帮助企业构建既满足当前需求又适应未来变化的弹性架构体系。

1 架构可持续性:从静态设计到动态演进

1.1 架构治理的范式转变

传统架构观将系统设计视为一次性活动,而现代架构实践强调持续演进的理念。根据行业数据,拥有成熟架构治理体系的企业在系统维护成本上比缺乏治理的组织低40%,新功能交付速度快35%。

架构可持续性的三大支柱

  • 质量属性守护:通过明确的质量标准防止架构腐化
  • 技术债主动管理:将债务治理融入日常开发流程
  • 演进式重构机制:在保证业务连续性的前提下持续优化

这种转变使架构工作从项目制活动转变为产品全生命周期的核心实践,确保了系统在整个生命周期内保持健康状态。

1.2 架构评审的价值重估

有效的架构评审不是障碍而是赋能,其核心价值体现在三个维度:

风险防控价值:提前识别设计缺陷,降低后期重构成本。数据表明,架构阶段发现的问题修复成本是编码阶段的1/10,生产环境的1/100。

知识传递价值:通过评审过程促进团队间架构共识,减少认知偏差。

质量内建价值:将架构原则和质量要求植入设计阶段,而非事后修补。

2 架构质量属性:可持续性的衡量基准

2.1 核心质量属性体系

架构质量属性为评审提供客观标准,避免主观判断的随意性。完整的质量属性体系涵盖多个维度:

运行期质量属性关注系统执行时的表现:

  • 性能:响应时间、吞吐量、资源利用率
  • 可靠性:故障率、可用性、容错能力
  • 安全性:数据保护、访问控制、漏洞防护

演进期质量属性影响系统变更和维护成本:

  • 可维护性:代码清晰度、模块化、文档完整性
  • 可扩展性:水平/垂直扩展能力、耦合度
  • 可测试性:单元测试覆盖率、集成测试便利性
// 可测试性设计示例:依赖注入提升可测试性
public class OrderService {
    private final PaymentGateway paymentGateway;
    private final InventoryService inventoryService;
    
    // 通过构造函数注入依赖,便于测试时mock
    public OrderService(PaymentGateway paymentGateway, InventoryService inventoryService) {
        this.paymentGateway = paymentGateway;
        this.inventoryService = inventoryService;
    }
    
    public boolean processOrder(Order order) {
        // 业务逻辑
        return true;
    }
}

依赖注入设计提升可测试性

2.2 质量属性的优先级权衡

不同业务场景下,质量属性的优先级需要差异化设置。一刀切的标准往往导致过度设计或质量不足。

系统类型关键质量属性次要质量属性权衡考量
电商交易一致性、可用性、性能可扩展性、可维护性强一致性可能降低性能
大数据平台可扩展性、吞吐量实时性、一致性最终一致性提升吞吐量
IoT边缘计算可靠性、安全性可维护性、性能离线能力优先于实时性

质量属性权衡框架帮助团队基于业务上下文做出合理决策:

# 质量属性权衡决策记录
decision_id: "perf-vs-maintainability"
context: "订单查询服务需要优化响应时间"
constraints: 
  - "必须在200ms内返回结果"
  - "团队规模小,维护成本需控制"
alternatives:
  - option: "引入缓存层"
    pros: ["性能提升明显"]
    cons: ["缓存一致性复杂化"]
  - option: "数据库查询优化"
    pros: ["架构简单"]
    cons: ["性能提升有限"]
decision: "采用缓存层,但增加缓存失效策略"
rationale: "业务要求性能优先,可通过工具降低维护成本"

架构决策记录模板

3 架构评审体系:多层次、全流程的质量保障

3.1 分层评审机制

有效的架构评审需要多层次覆盖,针对不同变更范围实施相应粒度的评审。

战术级评审针对日常技术决策和代码变更,通过轻量级流程保障基础质量:

  • 代码审查:每个PR必须经过至少一名核心成员审查
  • 设计讨论:复杂功能在实现前进行团队内设计评审
  • 工具辅助:静态分析、代码规范检查自动化

战略级评审针对系统级架构变更,通过正式流程保障一致性:

  • 架构委员会:跨部门专家组成,评审重大架构决策
  • 决策文档:使用ADR(Architecture Decision Record)记录关键决策
  • 影响分析:评估变更对现有系统的影响范围

混合评审模型平衡效率与质量控制:

graph TD
    A[变更请求] --> B{变更规模评估}
    B -->|小型变更| C[轻量评审]
    B -->|中型变更| D[团队评审]
    B -->|大型变更| E[架构委员会评审]
    C --> F[实施]
    D --> F
    E --> F
    F --> G[效果追踪]
    
    style C fill:#e1f5fe
    style D fill:#fff3e0
    style E fill:#f3e5f5

分层评审流程根据变更规模差异化处理

3.2 架构评审工作流设计

科学的评审流程确保效率效果的平衡。四步评审法是经过验证的有效方法:

初步评审阶段聚焦架构原则符合度,评估技术选型合理性。评审重点包括:

  • 技术栈与公司标准的一致性
  • 第三方组件成熟度与许可合规
  • 非功能需求的可实现性

详细设计阶段深入接口定义、数据模型和技术实现细节。关键检查点包括:

  • API设计是否符合RESTful规范或领域规范
  • 数据模型是否满足查询需求和一致性要求
  • 异常处理机制是否完备

最终评审阶段确认所有实施细节,评估风险和回滚方案。重点关注:

  • 实施计划的可操作性
  • 回滚方案的完备性
  • 监控和告警策略的覆盖度

实施监控阶段跟踪架构落地效果,及时发现问题。通过度量和复盘持续改进。

3.3 评审指标与成功标准

量化指标使架构评审客观可衡量,避免主观意见主导决策。

架构健康度指标

  • 耦合度:模块间依赖数量,衡量系统复杂度
  • 依赖稳定性:违反依赖规则的百分比
  • 架构一致分:代码实现与设计文档的一致性评分

技术债指标

  • 代码重复率:重复代码占总代码量的比例
  • 测试覆盖率:单元测试覆盖的代码比例
  • 文档完备率:API文档、设计文档的完整性

通过建立这些指标的基线目标和改进路线,架构评审从主观讨论转向数据驱动的决策过程。

4 技术债治理:从被动应对到主动管理

4.1 技术债的本质与分类

技术债是Ward Cunningham提出的隐喻,指为加速开发而采取的技术捷径所带来的长期成本。如同金融债务,技术债会产生"利息",即增加的维护成本。

技术债的四象限分类(Martin Fowler)提供系统化管理框架:

谨慎的(Prudent)鲁莽的(Reckless)
故意的(Deliberate)明知有更好方案但权衡后选择捷径明知是错误方案仍选择实施
无心的(Inadvertent)实施时不知有更好方案因知识不足而引入错误

技术债的三层结构帮助精准识别债务来源:

  • 代码级债务:代码坏味道、重复代码、复杂函数
  • 架构级债务:模块耦合过高、单点故障、技术栈落后
  • 基础设施债务:部署复杂、监控缺失、测试环境不稳定

4.2 技术债识别与评估体系

建立系统化识别机制是技术债治理的第一步。

自动化扫描工具持续检测技术债:

# 技术债扫描配置示例
technical_debt_scan:
  code_quality:
    - tool: sonarqube
      metrics: [complexity, duplication, code_smells]
  dependencies:
    - tool: dependabot
      metrics: [outdated_deps, security_vulnerabilities]
  architecture:
    - tool: structure101
      metrics: [cyclic_dependencies, modularity]

技术债评估矩阵基于影响和修复成本确定优先级:

-- 技术债优先级评估SQL示例
SELECT 
    debt_id,
    debt_type,
    impact_level,      -- 对业务的影响程度
    repair_cost,       -- 修复成本估算
    interest_cost,     -- 利息成本(每月额外维护成本)
    risk_exposure,     -- 风险暴露度
    (impact_level * risk_exposure) / repair_cost as priority_score
FROM technical_debts
WHERE status = 'identified'
ORDER BY priority_score DESC;

技术债优先级量化评估

4.3 技术债偿还策略

技术债治理需要多元化偿还策略,避免"一次性还清"的不切实际期望。

日常化偿还将技术债修复纳入正常开发节奏:

  • 男孩 Scout 规则:每次修改代码时使其比发现时更好
  • 技术债标签:在任务管理中标记技术债项目,纳入迭代计划
  • 专项修复迭代:定期安排专门的技术债修复周期

止损策略防止新债务产生:

  • 代码规范:通过静态检查防止新坏味道
  • 架构守护:通过依赖关系检查防止架构退化
  • 流水线门禁:质量门禁阻止债务积累

某大型互联网公司通过"20%时间用于技术债修复"的策略,在一年内将关键系统的平均复杂度降低30%,缺陷率下降45%。

5 演进式重构:可持续架构的实现路径

5.1 重构的策略选择

演进式重构强调小步快跑,通过持续的小规模改进避免大规模重写的高风险。

重构的时机选择至关重要:

  • 扩展功能时:在添加新功能时顺带重构相关模块
  • 修复缺陷时:理解代码逻辑后立即重构改善可读性
  • 代码审查时:发现设计问题立即提出重构建议
  • 定期维护窗口:专门安排重构时间块

重构风险控制策略

// 渐进式重构示例:通过特性开关降低风险
public class OrderService {
    private final FeatureToggle featureToggle;
    
    public Order processOrder(Order order) {
        if (featureToggle.isEnabled("new_processing_logic")) {
            return newOrderProcessing(order);
        } else {
            return legacyOrderProcessing(order);
        }
    }
    
    // 新逻辑逐步验证,可快速回退
    private Order newOrderProcessing(Order order) {
        // 重构后的实现
    }
}

通过特性开关实现渐进式重构

5.2 架构演进模式

不同架构风格需要不同的演进策略。

微服务架构演进

  • 绞杀者模式:逐步用新服务替换单体功能
  • 并行模式:新功能用新架构实现,旧功能逐步迁移
  • 分支化模式:通过抽象层兼容多版本实现

单体架构演进

  • 模块化先行:在单体内实施模块化,为拆分做准备
  • 数据库解耦:逐步拆分数据库,降低耦合度
  • 接口标准化:定义清晰接口,为未来微服务化铺路

成功的架构演进需要保持系统始终可发布,避免长期功能分支导致的合并困难。

6 风险评估框架:数据驱动的决策支持

6.1 风险识别与分类

架构风险需要系统化识别,而非依赖个人经验。

技术风险维度

  • 实现风险:技术方案可行性、团队技能匹配度
  • 集成风险:系统间兼容性、接口一致性
  • 性能风险:负载能力、资源消耗预估

管理风险维度

  • 进度风险:估算准确性、依赖任务进度
  • 资源风险:人员可用性、基础设施准备度
  • 范围风险:需求稳定性、变更频率

风险矩阵评估法量化风险影响:

graph LR
    A[风险识别] --> B[概率评估]
    A --> C[影响评估]
    B --> D[风险值计算]
    C --> D
    D --> E[优先级排序]
    
    style A fill:#f5f5f5
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#e8f5e8
    style E fill:#f3e5f5

风险矩阵评估流程

6.2 风险应对策略库

建立系统化应对策略提高风险处理效率。

风险规避:改变计划消除风险源头,如选择更成熟技术栈
风险转移:通过外包或保险将风险转嫁第三方
风险缓解:采取措施降低风险概率或影响,如增加测试
风险接受:对低概率或低影响风险明确接受并准备预案

架构决策风险检查表

risk_checklist:
  - id: "perf_risk"
    question: "是否进行性能压测?"
    mitigation: "制定性能测试计划"
  - id: "sec_risk"  
    question: "是否进行安全评估?"
    mitigation: "安排安全渗透测试"
  - id: "dep_risk"
    question: "是否有第三方依赖风险?"
    mitigation: "评估替代方案"

6.3 风险监控与预警

建立持续风险监控机制,及时发现新风险。

技术指标监控

  • 复杂度增长趋势:识别设计腐化早期信号
  • 构建失败频率:评估代码库稳定性
  • 测试覆盖率变化:衡量质量保障水平

过程指标监控

  • 迭代交付稳定性:评估团队交付节奏健康度
  • 缺陷逃逸率:衡量质量门禁有效性
  • 技术债增长率:监控债务积累速度

通过Dashboard可视化这些指标,团队可以实时掌握系统健康状况,及时干预潜在风险。

7 治理体系落地:从理论到实践

7.1 组织保障与文化培育

技术治理需要组织机制保障,而非依赖个人英雄主义。

架构治理委员会负责制定标准和评审重大决策:

  • 跨部门代表:确保各视角平衡
  • 定期会议机制:保证决策效率
  • 决策透明化:所有决策及理由公开可查

工程师文化培育使质量成为团队自觉追求:

  • 技术分享机制:定期分享架构经验教训
  • 代码评审文化:相互评审成为标准实践
  • 质量激励机制:奖励优秀技术贡献

7.2 工具链与平台支持

自动化工具是治理体系落地的加速器

架构治理工具链

# 架构治理工具栈示例
architecture_governance:
  design: 
    - tool: "structurizr"  # 架构图即代码
    - tool: "arc42"        # 架构文档模板
  analysis:
    - tool: "sonarqube"    # 代码质量分析
    - tool: "jqassistant"  # 架构规则检查
  decision:
    - tool: "adr-tools"    # 架构决策记录
  monitoring:
    - tool: "prometheus"   # 系统指标监控
    - tool: "grafana"      # 指标可视化

平台工程支持通过内部开发者平台降低架构治理成本:

  • 标准化模板:新项目基于最佳实践模板创建
  • 自助式工具:团队可自主进行架构分析
  • 质量门禁:流水线自动阻断不符合架构标准的变更

7.3 度量和反馈循环

建立闭环改进机制确保治理体系持续优化。

治理效能度量

  • 架构评审效率:从提交到决策的平均时间
  • 技术债解决率:已解决债务占总债务比例
  • 架构一致性:代码实现与设计文档的一致性

定期复盘机制

  • 季度架构评估:评估整体架构健康度
  • 案例深度分析:选择典型项目进行深度复盘
  • 治理流程优化:基于反馈优化评审流程和标准

某金融科技公司通过建立完整的架构治理体系,在两年内将系统平均可用性从99.9%提升至99.99%,新功能交付周期从月级缩短到周级。

总结

架构评审与技术债治理是现代软件工程的核心竞争力,它将系统架构从"一次性设计"转变为"持续演进过程"。通过质量属性定义、演进式重构和风险评估框架的协同作用,企业可以构建既满足当前业务需求又具备未来适应性的弹性架构体系。

成功治理的三要素

  1. 体系化思维:将架构治理视为完整体系而非孤立活动
  2. 数据驱动:基于度量而非主观感受做出决策
  3. 渐进式推进:小步快跑而非一次性完美主义

避免的常见陷阱

  • 过度治理:过多流程阻碍创新和效率
  • 形式主义:重文档轻实质,评审流于形式
  • 短期导向:忽视技术债积累的长期成本

架构治理的终极目标不是创建完美架构,而是建立持续改进的机制和能力,使系统能够随着业务需求和技术发展而有机演进。


📚 下篇预告
《数据平台全景与角色分工——OLTP、OLAP、批/流与数据湖的版图与边界》—— 我们将深入探讨:

  • 🗄️ 数据架构演进:从传统数据库到现代数据平台的技术路径
  • 处理范式:OLTP事务处理、OLAP分析计算、批处理与流处理的适用场景
  • 🏗️ 数据湖与数据仓库:逻辑架构、存储分层与查询优化策略
  • 👥 角色协作:数据工程师、分析师、科学家在数据平台中的职责边界
  • 🔄 流水线设计:数据采集、加工、服务与治理的全链路管理

点击关注,构建高效可靠的数据平台体系!

今日行动建议

  1. 评估当前系统架构质量属性,建立可量化的健康度指标体系
  2. 制定技术债识别和分类标准,建立债务台账和偿还计划
  3. 设计分层架构评审机制,平衡控制力度和团队自主性
  4. 引入演进式重构实践,将架构改进融入日常开发流程
  5. 建立架构风险评估框架,数据驱动技术决策

JExten:基于Java模块系统(JPMS)构建健壮的插件架构

1. 动机:通往模块化隔离之路

在Java中构建可扩展应用程序时,开发者常常从一个简单的问题开始:"如何让用户无需重新编译核心应用程序就能添加功能?" 旅程通常始于标准的 java.util.ServiceLoader,它提供了一种发现接口实现的简单机制。

然而,随着应用程序的增长,一个关键问题出现了:"类路径地狱"。

想象一下,你有一个使用 library-v1 的主机应用程序。你创建了一个插件系统,有人写了一个需要 library-v2 的 "Twitter 插件"。如果所有东西都在同一个扁平的类路径上运行,就会产生冲突。要么主机因为得到错误的库版本而崩溃,要么插件失败。你无法在类路径上同时存在同一个库的两个版本而不面临运行时异常(如 ClassDefNotFoundErrorNoSuchMethodError)的风险。

这正是JExten背后的核心驱动力。我需要一种能够严格封装插件的方式,使得每个插件都可以定义自己的依赖,而不影响主机或其他插件。

引入JPMS(Java平台模块系统)

Java 9引入了模块系统(JPMS),它提供了强封装和显式的依赖关系图。它允许我们创建隔离的模块"层"。

  • 启动层:JVM和平台模块。
  • 主机层:核心应用程序及其依赖。
  • 插件层:在主机层之上动态创建的层。

通过利用JPMS的ModuleLayers,JExten允许插件A依赖于Jackson 2.14,而插件B依赖于Jackson 2.10,两者可以在同一个运行的应用程序中和睦共存。

2. 架构与设计

JExten设计为轻量级且基于注解驱动,抽象了原始ModuleLayers的复杂性,同时提供了依赖注入(DI)和生命周期管理等强大功能。

架构基于三个主要支柱:

扩展模型

核心在于,JExten在"契约"(API)和"实现"之间进行了清晰分离。

    1. 扩展点 (@ExtensionPoint):在主机应用程序(或共享API模块)中定义的接口,规定了哪些功能可以被扩展。

      @ExtensionPoint(version = "1.0")
      public interface PaymentGateway {
        void process(double amount);
      }
    1. 扩展 (@Extension):由插件提供的具体实现。

      @Extension(priority = Priority.HIGH)
      public class StripeGateway implements PaymentGateway {
        // ...
      }

      注意,你可以在没有PluginManager的情况下使用ExtensionManager。这在测试中或当你希望在非插件环境中使用JExten,且所有扩展都已经在模块路径中可用时非常有用。

管理器分离

为了分离关注点,该库将职责划分到两个不同的管理器:

  1. PluginManager("物理层")

    • 该组件处理原始工件(JAR/ZIP文件)。
    • 它使用SHA-256校验和验证完整性,确保插件未被篡改。
    • 它构建JPMS ModuleLayer 图。它读取 plugin.yaml 清单,解析依赖项(从本地缓存或Maven仓库),并构建类加载环境。
  2. ExtensionManager("逻辑层")

    • 一旦层构建完成,该组件接管。
    • 它在各个层中扫描带有 @Extension 注解的类。
    • 它管理这些扩展的生命周期(单例、会话或原型作用域)。
    • 它处理依赖注入。

依赖注入

由于插件在隔离的层中运行,标准的DI框架(如Spring或Guice)有时可能"太重"或在动态模块边界间配置起来很棘手。JExten包含一个内置的、轻量级的DI系统。

你可以简单地使用 @Inject 将扩展连接在一起:

@Extension
public class MyPluginService {
    @Inject
    private PaymentGateway gateway; // 自动注入最高优先级的实现
}

这在模块边界之间可以无缝工作。一个插件可以注入由主机提供的服务,甚至可以注入由另一个插件提供的服务(如果模块关系图允许的话)。

3. 使用示例

下面快速了解一下如何定义扩展点,在插件中实现它,并在应用程序中使用。

I. 定义一个扩展点

创建一个接口并用 @ExtensionPoint 注解。这是插件将实现的契约。

@ExtensionPoint(version = "1.0")
public interface Greeter {
    void greet(String name);
}

II. 实现一个扩展

在你的插件模块中,实现该接口并用 @Extension 注解。

@Extension
public class FriendlyGreeter implements Greeter {
    @Override
    public void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }
}

III. 发现与使用

在你的主机应用程序中,使用 ExtensionManager 来发现和调用扩展。

public class Main {
    public static void main(String[] args) {
        // 初始化管理器
        ExtensionManager manager = ExtensionManager.create(pluginManager);

        // 获取Greeter扩展点的所有扩展
        manager.getExtensions(Greeter.class)
               .forEach(greeter -> greeter.greet("World"));
    }
}

IV. 将你的扩展打包为插件

最后,使用 jexten-maven-plugin Maven插件在编译时检查你的 module-info.java,并将你的扩展打包成一个包含所有依赖项和生成的 plugin.yaml 清单的ZIP包。

<plugin>
    <groupId>org.myjtools.jexten</groupId>
    <artifactId>jexten-maven-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <execution>
            <goals>
                <goal>generate-manifest</goal>
                <goal>assemble-bundle</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <hostModule>com.example.app</hostModule>
    </configuration>
</plugin>

然后,你可以将生成的ZIP包安装到你的主机应用程序中:

public class Application {
    public static void main(String[] args) throws IOException {
        Path pluginDir = Path.of("plugins");

        // 创建插件管理器
        PluginManager pluginManager = new PluginManager(
            "org.myjtools.jexten.example.app", // 应用程序ID
            Application.class.getClassLoader(),
            pluginDir
        );

        // 从ZIP包安装插件
        pluginManager.installPluginFromBundle(
            pluginDir.resolve("my-plugin-1.0.0.zip")
        );

        // 创建支持插件的扩展管理器
        ExtensionManager extensionManager = ExtensionManager.create(pluginManager);

         // 从插件获取扩展
        extensionManager.getExtensions(Greeter.class)
            .forEach(greeter -> greeter.greet("World"));
    }
}

4. 与其他解决方案的比较

选择合适的插件框架取决于你的具体需求。以下是JExten与一些成熟替代方案的对比:

PF4J (Plugin Framework for Java)

PF4J是一个成熟的、轻量级的插件框架,依赖于ClassLoader隔离。

  • 隔离性:PF4J使用自定义ClassLoader隔离插件。JExten使用JPMS ModuleLayers。后者是自Java 9以来处理隔离的"原生"Java方式,在JVM级别严格执行封装。
  • 现代性:虽然PF4J非常优秀,但JExten是专门为现代模块化Java生态系统(Java 21+)设计的,利用模块描述符(module-info.java)来定义依赖关系,而不是自定义清单。

OSGi

OSGi是模块化的黄金标准,为Eclipse等IDE提供支持。

  • 复杂性:OSGi功能强大,但学习曲线陡峭且样板代码多(Manifest头、Activators、复杂的服务动态)。JExten通过专注于80%的用例——具有简单依赖注入的严格隔离扩展——提供了远低于OSGi的复杂性("精简版OSGi"),且不需要完整的OSGi容器。
  • 运行时:OSGi带来沉重的运行时。JExten是一个轻量级库,构建在标准JVM特性之上。

Layrry

Layrry是一个用于执行模块化Java应用程序的启动器和API。

  • 关注点:Layrry非常侧重于模块层的配置和组装(通常通过YAML/TOML),并充当运行器。JExten则侧重于这些层内的编程模型
  • 特性:Layrry擅长构建层,但它不提供固化的应用框架。JExten提供了"粘合"代码——扩展点、依赖注入和生命周期管理——这些是你在使用原始模块层或Layrry时必须自己编写的东西。
特性JExtenPF4JOSGiLayrry
隔离JPMS 模块层文件/类加载器Bundle类加载器JPMS 模块层
配置Java 注解属性/清单Manifest 头部YAML/TOML
依赖注入内置 (@Inject)外部 (Spring/Guice)声明式服务无 (ServiceLoader)
学习曲线

5. 结论

JExten是一个轻量级、基于注解驱动的插件框架,它利用JPMS模块层来提供隔离和依赖管理。其设计目标是易于使用和理解,注重简洁性和易用性。

最后,请记住JExten仍处于早期阶段,有很大的改进空间。欢迎在GitHub上为项目做贡献和/或在issue部分参与讨论。项目仓库链接在此处


【注】本文译自:JExten: Building a Robust Plugin Architecture with Java Modules (JPMS) - DEV Community