You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus多模块项目循环引用问题 求CDI依赖注入解决方案

Quarkus多模块项目循环依赖与CDI注入解决方案

我首次用Quarkus开发多模块项目,父POM的模块结构如下:

<modules>
    <module>domain</module>
    <module>application</module>
    <module>client</module>
</modules>

domain模块存放需要由application和client模块实现的接口,因此application和client模块都添加了domain的依赖来实现接口。但尝试在domain模块中添加application的依赖以实现CDI注入时,出现了循环引用错误:

[ERROR] [ERROR] The projects in the reactor contain a cyclic reference: Edge between 'Vertex{label='com.test.name:application:0.0.1-SNAPSHOT'}' and 'Vertex{label='com.test.name:domain:0.0.1-SNAPSHOT'}' introduces to cycle in the graph com.test.name:domain:0.0.1-SNAPSHOT --> com.test.name:application:0.0.1-SNAPSHOT --> com.test.name:domain:0.0.1-SNAPSHOT @
[ERROR] The projects in the reactor contain a cyclic reference: Edge between 'Vertex{label='com.test.name:application:0.0.1-SNAPSHOT'}' and 'Vertex{label='com.test.name:domain:0.0.1-SNAPSHOT'}' introduces to cycle in the graph com.test.name:domain:0.0.1-SNAPSHOT --> com.test.name:application:0.0.1-SNAPSHOT --> com.test.name:domain:0.0.1-SNAPSHOT -> [Help 1]

核心原则:domain层不能依赖实现层

domain作为接口、领域模型的定义层,必须保持对实现层(application/client)的独立,循环依赖本质是违反了依赖倒置原则,解决思路是靠CDI的特性完成接口与实现的绑定,而非硬编码依赖。

方案1:@Inject + @Qualifier(推荐)

这是最符合Quarkus CDI设计的方案:

  1. 在domain模块定义接口和自定义Qualifier(用于区分不同实现):
// domain模块 - 业务接口
public interface BusinessService {
    void execute();
}

// domain模块 - 自定义限定符,标记application层的实现
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
public @interface AppImpl {
}
  1. 在application模块实现接口并标记Qualifier:
// application模块 - 接口实现类
@AppImpl
@Singleton // 按需选择CDI作用域,如@RequestScoped
public class AppBusinessServiceImpl implements BusinessService {
    @Override
    public void execute() {
        // 业务逻辑实现
    }
}
  1. 在需要注入的地方(比如application模块的业务类)直接注入:
// application模块中的类
@Inject
@AppImpl
BusinessService businessService;

// 使用实例
public void process() {
    businessService.execute();
}

此时application依赖domain,domain不依赖任何实现层,完全避免循环。

方案2:拆分职责,剥离domain中的依赖代码

如果domain模块中存在需要依赖实现类的逻辑,说明domain的职责边界不清晰,应该把这部分代码拆分到新的模块(比如infrastructure或base):

  • 新建infrastructure模块,依赖domain
  • application模块依赖infrastructure和domain
  • 将需要注入实现的代码移到infrastructure模块,application模块提供实现并注入到infrastructure的类中,彻底打破循环。

方案3:用Instance动态查找实现类

如果确实需要在domain模块中获取实现类,可借助CDI的Instance<T>动态查找,无需依赖application模块:

  1. 在domain模块添加Quarkus CDI API依赖(仅API,不依赖实现):
<!-- domain模块的pom.xml -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-arc</artifactId>
    <scope>provided</scope>
</dependency>
  1. 在domain模块的类中使用Instance<T>动态获取实现:
// domain模块中的类
@Inject
Instance<BusinessService> serviceInstance;

public void useService() {
    // 通过Qualifier筛选出application层的实现
    BusinessService service = serviceInstance.select(AppImpl.class).get();
    service.execute();
}

运行时只要application模块被加载,CDI容器就能找到对应的实现类,编译时domain无需依赖application,避免循环。


内容的提问来源于stack exchange,提问作者Clément Gibert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:40:38