Java HK2:从子定位器访问运行级别作用域服务
我在JavaSE应用程序中使用Jersey。HK2向整个应用程序提供依赖注入。服务在应用程序服务定位器中注册,该定位器是Jersey服务定位器的父级Java HK2:从子定位器访问运行级别作用域服务,java,jersey,hk2,Java,Jersey,Hk2,我在JavaSE应用程序中使用Jersey。HK2向整个应用程序提供依赖注入。服务在应用程序服务定位器中注册,该定位器是Jersey服务定位器的父级 + application locator |\- RunLevel capabilities | - MyCustomService, @RunLevel(value=1) \ + jersey locator \- jersey resource class \ @Inject MyCustomService 我的问题是
+ application locator
|\- RunLevel capabilities
| - MyCustomService, @RunLevel(value=1)
\
+ jersey locator
\- jersey resource class
\ @Inject MyCustomService
我的问题是我无法从Jersey内部访问运行级别范围的服务。在上面的示例中,当jersey资源打开时,MyCustomService
的注入失败:
java.lang.IllegalStateException:找不到org.glassfish.hk2.runlevel.runlevel的活动上下文
原因似乎是HK2运行级功能背后的服务具有可见性:jersey定位器无法通过其父定位器访问它们。看
问题:
- 为什么运行级功能的服务在可见性方面受到限制
- 我能做些什么来克服这个问题
- JavaSE应用程序启动。默认初始运行级别为-1,目标运行级别为3。在通往那里的路上,必须成功地通过不同的阶段才能继续
- 在运行级别1,建立到相关外部应用程序(数据库、memcache、MessageBroker等)的连接
- 在运行级别2,启动用于后台处理的执行器服务和HTTP服务(运行jersey)。Jersey拒绝此级别的所有传入请求
- 在运行级别3,
附加到代理,将请求提供给后台执行器。Jersey接受并处理HTTP请求MessageListeners
这个概念允许对可用性和长时间运行的请求进行粒度控制。关闭时,应用程序将处于运行级别2,直到完成以前接受的HTTP请求并完成排队的后台任务。但是,不接受任何新任务/请求。然后,运行级别1,0,-1,退出。关于子级和运行级别服务的想法是,实际的运行级别服务将是子定位器中的精简服务,该定位器协调父级中的实际服务。一个流程中的多个“子系统”可能有不同的RunLevelService“隔间”,每个隔间都位于它自己的父级子级中 在该模型中,您在子对象中启动/添加RunLevelService,而不是在父对象中。通过这种方式,您可以在同一父级的不同子级中拥有不同级别的多个RunLevelServices
不过,听起来您有一个不同的用例,它可能无法完全工作。值得考虑您的用例。更新:这不起作用强> 出于教育目的,我将把它留在这里,但不要这样做一旦从jersey的服务定位器中访问
RunLevelController
,它将失去对它在另一个服务定位器上管理的所有服务的跟踪
+ application locator
|\- RunLevel capabilities
| - MyCustomService, @RunLevel(value=1)
\
+ jersey locator
\- jersey resource class
\ @Inject MyCustomService
更新结束
我已经解决了这个问题:我故意覆盖LOCAL
可见性,并将所需的描述符插入缺少它们的服务定位器
public class RunLevelBridge implements Feature {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ServiceLocator sourceLocator;
public RunLevelBridge(ServiceLocator sourceLocator) {
this.sourceLocator = sourceLocator;
}
@Override
public boolean configure(FeatureContext context) {
if (sourceLocator == null) {
log.error("Unable to bridge descriptors, the source service locator cannot be null");
return false;
}
InjectionManager im = InjectionManagerProvider.getInjectionManager(context);
ServiceLocator jerseyLocator = im.getInstance(ServiceLocator.class);
if (jerseyLocator == null) {
log.error("Unable to bridge descriptors, the target service locator cannot be null");
return false;
}
if (!sourceLocator.equals(jerseyLocator.getParent())) {
ExtrasUtilities.bridgeServiceLocator(jerseyLocator, sourceLocator);
log.info("Bridge from {} into {} established", sourceLocator.getName(), jerseyLocator.getName());
}
Filter filter;
filter = BuilderHelper.createContractFilter(RunLevelContext.class.getName());
sourceLocator.getDescriptors(filter).forEach(
(descriptor) -> ServiceLocatorUtilities.addOneDescriptor(
jerseyLocator,
descriptor,
false
)
);
filter = BuilderHelper.createContractFilter(RunLevelController.class.getName());
sourceLocator.getDescriptors(filter).forEach(
(descriptor) -> ServiceLocatorUtilities.addOneDescriptor(
jerseyLocator,
descriptor,
false
)
);
log.info("Added the RunLevel feature to jersey's service locator");
return true;
}
}
网桥已在ResourceConfig
中注册:
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig(ServiceLocator parentLocator) {
// bridge runlevel into jersey
register(new RunLevelBridge(parentLocator));
// ... other stuff ...
}
}
我鼓励对这种非正统方法进行反馈。解决方案是尊重
描述符可视性#LOCAL
并仅从管理它们的服务定位器注入依赖于运行级别上下文的服务
这有点麻烦:
- 从Jersey资源中,按名称获取具有运行级别功能的服务定位器
- 获取明确注入的运行级别作用域服务
ServiceLocator applicationLocator = ServiceLocatorFactory.getInstance().find("applicationLocator");
MyCustomService mcs = applicationLocator.getService(MyCustomService.class);
mcs.doSomething();
为了减少忘记这样做的危险,只需将MyCustomService
注入jersey资源,我现在将运行级别范围内的服务也标记为DescriptorVisibility\LOCAL
。这样他们就不能被球衣定位器注射。谢谢,我希望你能回答。我爱死你了!我已经更新了这个问题,以提供运行级别的上下文重用。如您所见,jersey(及其子定位器)在运行级概念中运行,而不是相反。你能给我什么建议吗?不幸的是,我对泽西的职业了解不多。对我来说,它正在使用运行级服务是个新闻对不起,我的解释不好。我的代码从运行级服务启动Grizzly/Jersey容器。是的,我的意思是,这正是运行级服务的设计目的。我们在GlassFish和WebLogic中做的事情几乎完全相同(我们有更多的级别,但想法相同)