如何在Quarkus调度器中调用依赖RequestScoped Bean的服务?
问题描述
我有一个REST端点,调用依赖RequestScoped Bean的GreetingService,通过REST请求调用一切正常。但用Quarkus Scheduler创建定时任务定期调用该服务时,即便加了@ActivateRequestContext注解,还是抛出java.lang.IllegalStateException: No RESTEasy Reactive request in progress异常。
我不想修改现有服务(服务规模较大),也没法用外部定时任务(有权限控制问题),希望在调度器内部解决。目前发现用RestAssured调用端点的方式可行,但把测试依赖引入主代码是否合理?
可复现代码
资源类
@Path("/hello") public class GreetingResource { @Inject GreetingService greetingService; @GET public String hello() { return greetingService.getGreetings(); } }
服务类
@ApplicationScoped public class GreetingService { @Inject RequestBean requestBean; public String getGreetings() { //do logic, call other services, save to DB, etc if (requestBean.getAcceptLanguage().equals("en")) { return "Hello from RESTEasy Reactive"; } else { return "Hola desde RESTEasy Reactive"; } } }
请求作用域Bean
@RequestScoped public class RequestBean { private String acceptLanguage; RequestBean(HttpHeaders httpHeaders) { acceptLanguage = httpHeaders.getHeaderString(HttpHeaders.ACCEPT_LANGUAGE); if (acceptLanguage == null || acceptLanguage.isBlank()) { this.acceptLanguage = "en"; } } public String getAcceptLanguage() { return acceptLanguage; } }
调度器类
@ApplicationScoped public class Scheduler { @Inject GreetingService greetingService; @Scheduled(every = "10s") @ActivateRequestContext public void scheduledTask() { try { Log.info("Scheduler is starting"); greetingService.getGreetings(); Log.info("Scheduler is finished"); } catch (Exception e) { Log.error("Error while running scheduler", e); } } }
相关依赖
<quarkus.platform.version>3.8.5</quarkus.platform.version> ... <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-scheduler</artifactId> </dependency>
解决方案
1. 不推荐引入RestAssured到主代码
RestAssured本质是测试工具,引入主代码会增加不必要的依赖体积,还可能带来测试相关的类冲突,不符合生产代码的依赖规范,不建议这么做。
2. 手动模拟RESTEasy Reactive请求上下文
@ActivateRequestContext仅能激活CDI请求上下文,但无法提供RESTEasy Reactive所需的请求对象,因此需要手动构建完整的请求上下文。修改调度器类如下:
@ApplicationScoped public class Scheduler { @Inject GreetingService greetingService; @Inject RestEasyReactiveRequestContextFactory requestContextFactory; @Scheduled(every = "10s") public void scheduledTask() { try { Log.info("Scheduler is starting"); // 构建模拟的HTTP请求头,可根据需求自定义 Map<String, String> headers = new HashMap<>(); headers.put(HttpHeaders.ACCEPT_LANGUAGE, "en"); // 创建并激活RESTEasy Reactive请求上下文 try (RestEasyReactiveRequestContext ctx = requestContextFactory.create( new MockHttpRequest().headers(headers), new MockHttpResponse())) { greetingService.getGreetings(); } Log.info("Scheduler is finished"); } catch (Exception e) { Log.error("Error while running scheduler", e); } } }
- 所需类
RestEasyReactiveRequestContextFactory、MockHttpRequest、MockHttpResponse均属于quarkus-resteasy-reactive自带依赖,无需额外引入; - 可根据定时任务需求调整请求头参数,比如设置不同的
Accept-Language默认值。
3. 替代方案:为RequestBean添加适配逻辑(极小修改)
如果允许对RequestBean做不影响现有逻辑的微小修改,可以添加一个供定时任务使用的构造方法:
@RequestScoped public class RequestBean { private String acceptLanguage; // 原有REST请求用构造方法 RequestBean(HttpHeaders httpHeaders) { acceptLanguage = httpHeaders.getHeaderString(HttpHeaders.ACCEPT_LANGUAGE); if (acceptLanguage == null || acceptLanguage.isBlank()) { this.acceptLanguage = "en"; } } // 定时任务场景用构造方法,标注为备选实现 @Alternative @Priority(1) public RequestBean() { this.acceptLanguage = "en"; // 设置默认值 } public String getAcceptLanguage() { return acceptLanguage; } }
之后在调度器中通过@Inject @Any Instance<RequestBean>选择这个备选实现即可。但如果你完全不想修改现有服务,优先选择方案2。
内容的提问来源于stack exchange,提问作者Janez Radešček
相关产品推荐
相关产品推荐

