Quarkus传统RestEasy中@RestClient与Mock共存引发依赖不满足错误
在Quarkus传统RestEasy环境下,当通过@RestClient注入标注了@RegisterRestClient的FlightGateway接口时,若项目中存在该接口的实现类FlightMock,会触发UnsatisfiedResolutionException(依赖不满足)错误。移除FlightMock对FlightGateway的实现后,应用可正常启动;且该问题仅出现在传统RestEasy中,RestEasy Reactive无此异常。
相关代码示例
网关接口(FlightGateway)
@RegisterRestClient(configKey = "gateway") public interface FlightGateway { @Path("/esb/flight/bookflight/v1") @POST BookFlightResp bookFlight(BookFlightReq req); }
服务实现类(FlightServiceImpl)
public class FlightServiceImpl implements FlightService { @RestClient FlightGateway flightGateway; @Override public BookFlightRply flightBooking(BookFlight req) { BookFlightRply rply = flightGateway.bookFlight(req); return rply; } }
Mock实现类(FlightMock)
public class FlightMock implements FlightGateway { @Override public BookFlightResp bookFlight(BookFlightReq req) { // Mock逻辑 return resp; } }
报错信息
[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type com.as.flight.integration.gateway.FlightGateway and qualifiers [@RestClient] - java member: com.as.flight.service.FlightServiceImpl#FlightGateway [[1;31mERROR[m] - declared on CLASS bean [types=[com.as.flight.service.FlightServiceImpl, java.lang.Object, com.as.flight.service.FlightService], qualifiers=[@Default, @Any], target=com.as.flight.service.FlightServiceImpl]
问题原因
传统RestEasy的RestClient扩展在处理带有@RegisterRestClient的接口时,若发现该接口存在自定义实现类(如FlightMock),会停止自动生成RestClient代理Bean。此时CDI容器中仅存在无@RestClient限定符的FlightMock Bean,而注入点要求的是带有@RestClient限定符的FlightGateway实例,因此触发依赖解析失败。
而RestEasy Reactive的RestClient扩展逻辑不同,即使接口存在自定义实现类,仍会生成带@RestClient限定符的代理Bean,因此不会出现冲突。
解决方案
方案1:为Mock类添加限定符并标记为CDI Bean
给FlightMock添加自定义限定符,同时标记为CDI Bean,确保其与RestClient代理Bean的限定符区分开:
// 自定义限定符 @Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface Mock {} // 在FlightMock上使用 @ApplicationScoped @Mock public class FlightMock implements FlightGateway { // ... 实现逻辑 }
此时注入点明确使用@RestClient,CDI容器会正确匹配RestClient代理Bean。
方案2:将Mock类放在测试目录中
如果FlightMock仅用于测试场景,直接将其移至src/test/java目录下。生产环境构建时不会扫描测试目录的类,因此不会干扰RestClient代理Bean的生成。
方案3:通过Quarkus Profile控制Mock类的加载
为FlightMock添加@Profile注解,指定仅在特定Profile(如测试环境)下启用该Bean:
@ApplicationScoped @Profile("test") public class FlightMock implements FlightGateway { // ... 实现逻辑 }
这样在生产环境(默认Profile)下,FlightMock不会被注册为CDI Bean,RestClient代理Bean可正常生成。
方案4:使用@Alternative替代默认实现
将FlightMock标记为@Alternative并设置优先级,在需要Mock的场景下启用:
@ApplicationScoped @Alternative @Priority(1) public class FlightMock implements FlightGateway { // ... 实现逻辑 }
然后在application.properties中配置启用Alternative:
quarkus.arc.alternatives.enabled=com.as.flight.integration.mock.FlightMock
此方式适用于需要在特定环境替换RestClient实现的场景。
内容的提问来源于stack exchange,提问作者topgun

