如何使 HK2 ServiceLocator 使用桥接自 ServiceLocator 的单例服务实例?
How do I make a HK2 ServiceLocator use the Singleton service instances from the ServiceLocator that it's bridged from?
我们正在使用 ExtrasUtilities.bridgeServiceLocator()
通过将应用 ServiceLocator 桥接到 Jersey ServiceLocator,将在一个 ServiceLocator 中创建的现有单例应用程序服务注入 Jersey RESTful Web 服务。
但是 'outer' 定位器中存在的单例未被使用 - 每个服务在注入 Jersey 服务时都会再次创建。似乎 Singleton 仅在 ServiceLocator 的范围内可见,即使它是桥接的。
这是预期的行为吗?如果是的话,有没有什么办法可以改变这种行为,并在桥接的 ServiceLocators 中拥有一个真正的单例?
我已将问题提取到一组测试中 类 来说明以下要点:
public class BridgedServiceTest
{
private ServiceLocator _outerServiceLocator;
private ServiceLocator _innerServiceLocator;
@Test
public void testBridgedInnerServiceOK() throws Exception
{
ServiceLocatorFactory serviceLocatorFactory = ServiceLocatorFactory.getInstance();
_outerServiceLocator = serviceLocatorFactory.create("Outer");
ServiceLocatorUtilities.addClasses(_outerServiceLocator, SingletonServiceImpl.class);
_innerServiceLocator = serviceLocatorFactory.create("Inner");
ExtrasUtilities.bridgeServiceLocator(_innerServiceLocator, _outerServiceLocator);
final Client client1 = new Client();
_outerServiceLocator.inject(client1);
assertThat(SingletonServiceImpl.instanceCount.get(), Matchers.is(1));
client1.test();
final Client client2 = new Client();
_innerServiceLocator.inject(client2);
// next line fails as instance count is 2
assertThat(SingletonServiceImpl.instanceCount.get(), Matchers.is(1));
client2.test();
}
@After
public void tearDown() throws Exception
{
_innerServiceLocator.shutdown();
_outerServiceLocator.shutdown();
}
}
@Contract
public interface SingletonService
{
void fulfil();
}
@Service
public class SingletonServiceImpl implements SingletonService
{
public static AtomicInteger instanceCount = new AtomicInteger();
@PostConstruct
public void postConstruct()
{
instanceCount.incrementAndGet();
}
@Override
public void fulfil()
{
System.out.println("Contract Fulfilled.");
}
}
public class Client
{
@Inject
private SingletonService _singletonService;
public void test()
{
_singletonService.fulfil();
}
public SingletonService getSingletonService()
{
return _singletonService;
}
}
与此同时(如果确实是设计行为),我通过实施自己的 InjectionResolver 实现了解决方法。这不是一个完整的解决方案,因为它只适用于注入(即调用 ServiceLocator.getService() 不会桥接)但它可以满足我现在的需要。
这里是 class 如果有人需要的话。而不是调用 ExtrasUtilities.bridgeServiceLocator(_innerServiceLocator, _outerServiceLocator);
你调用 BridgingInjectionResolver.bridgeInjection(_innerServiceLocator, _outerServiceLocator);
@Singleton
@Rank(1)
public class BridgingInjectionResolver implements InjectionResolver<Inject>
{
@Inject
private ServiceLocator _localServiceLocator;
private ServiceLocator _remoteServiceLocator;
public BridgingInjectionResolver()
{
}
/**
* This method will bridge injection of all non-local services from the from ServiceLocator into the into
* ServiceLocator. The two ServiceLocators involved must not have a parent/child relationship
*
* @param into The non-null ServiceLocator that will be able to inject services from the from ServiceLocator
* @param from The non-null ServiceLocator that will provide services for injection to the into ServiceLocator
*/
public static void bridgeInjection(ServiceLocator into, ServiceLocator from)
{
checkParentage(into, from);
checkParentage(from, into);
ServiceLocatorUtilities.addClasses(into, BridgingInjectionResolver.class);
into.getService(BridgingInjectionResolver.class).setRemoteServiceLocator(from);
}
private static void checkParentage(ServiceLocator a, ServiceLocator b)
{
ServiceLocator originalA = a;
while (a != null) {
if (a.getLocatorId() == b.getLocatorId()) {
throw new IllegalStateException("Locator " + originalA + " is a child of or is the same as locator " + b);
}
a = a.getParent();
}
}
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> root)
{
ActiveDescriptor<?> ad = _localServiceLocator.getInjecteeDescriptor(injectee);
if (ad != null) {
return _localServiceLocator.getService(ad, root, injectee);
}
ad = _remoteServiceLocator.getInjecteeDescriptor(injectee);
if ((ad != null) && (ad.getDescriptorVisibility() == DescriptorVisibility.LOCAL)) {
ad = null;
}
if (ad == null) {
if (injectee.isOptional()) {
return null;
}
throw new MultiException(new UnsatisfiedDependencyException(injectee));
}
return _remoteServiceLocator.getService(ad, root, injectee);
}
@Override
public boolean isConstructorParameterIndicator()
{
return false;
}
@Override
public boolean isMethodParameterIndicator()
{
return false;
}
private void setRemoteServiceLocator(ServiceLocator remoteServiceLocator)
{
_remoteServiceLocator = remoteServiceLocator;
}
}
ServiceLocator 桥中存在错误,该错误已得到修复。修复将在 hk2 2.5.0-b07 或更高版本中进行!
我们正在使用 ExtrasUtilities.bridgeServiceLocator()
通过将应用 ServiceLocator 桥接到 Jersey ServiceLocator,将在一个 ServiceLocator 中创建的现有单例应用程序服务注入 Jersey RESTful Web 服务。
但是 'outer' 定位器中存在的单例未被使用 - 每个服务在注入 Jersey 服务时都会再次创建。似乎 Singleton 仅在 ServiceLocator 的范围内可见,即使它是桥接的。
这是预期的行为吗?如果是的话,有没有什么办法可以改变这种行为,并在桥接的 ServiceLocators 中拥有一个真正的单例?
我已将问题提取到一组测试中 类 来说明以下要点:
public class BridgedServiceTest
{
private ServiceLocator _outerServiceLocator;
private ServiceLocator _innerServiceLocator;
@Test
public void testBridgedInnerServiceOK() throws Exception
{
ServiceLocatorFactory serviceLocatorFactory = ServiceLocatorFactory.getInstance();
_outerServiceLocator = serviceLocatorFactory.create("Outer");
ServiceLocatorUtilities.addClasses(_outerServiceLocator, SingletonServiceImpl.class);
_innerServiceLocator = serviceLocatorFactory.create("Inner");
ExtrasUtilities.bridgeServiceLocator(_innerServiceLocator, _outerServiceLocator);
final Client client1 = new Client();
_outerServiceLocator.inject(client1);
assertThat(SingletonServiceImpl.instanceCount.get(), Matchers.is(1));
client1.test();
final Client client2 = new Client();
_innerServiceLocator.inject(client2);
// next line fails as instance count is 2
assertThat(SingletonServiceImpl.instanceCount.get(), Matchers.is(1));
client2.test();
}
@After
public void tearDown() throws Exception
{
_innerServiceLocator.shutdown();
_outerServiceLocator.shutdown();
}
}
@Contract
public interface SingletonService
{
void fulfil();
}
@Service
public class SingletonServiceImpl implements SingletonService
{
public static AtomicInteger instanceCount = new AtomicInteger();
@PostConstruct
public void postConstruct()
{
instanceCount.incrementAndGet();
}
@Override
public void fulfil()
{
System.out.println("Contract Fulfilled.");
}
}
public class Client
{
@Inject
private SingletonService _singletonService;
public void test()
{
_singletonService.fulfil();
}
public SingletonService getSingletonService()
{
return _singletonService;
}
}
与此同时(如果确实是设计行为),我通过实施自己的 InjectionResolver 实现了解决方法。这不是一个完整的解决方案,因为它只适用于注入(即调用 ServiceLocator.getService() 不会桥接)但它可以满足我现在的需要。
这里是 class 如果有人需要的话。而不是调用 ExtrasUtilities.bridgeServiceLocator(_innerServiceLocator, _outerServiceLocator);
你调用 BridgingInjectionResolver.bridgeInjection(_innerServiceLocator, _outerServiceLocator);
@Singleton
@Rank(1)
public class BridgingInjectionResolver implements InjectionResolver<Inject>
{
@Inject
private ServiceLocator _localServiceLocator;
private ServiceLocator _remoteServiceLocator;
public BridgingInjectionResolver()
{
}
/**
* This method will bridge injection of all non-local services from the from ServiceLocator into the into
* ServiceLocator. The two ServiceLocators involved must not have a parent/child relationship
*
* @param into The non-null ServiceLocator that will be able to inject services from the from ServiceLocator
* @param from The non-null ServiceLocator that will provide services for injection to the into ServiceLocator
*/
public static void bridgeInjection(ServiceLocator into, ServiceLocator from)
{
checkParentage(into, from);
checkParentage(from, into);
ServiceLocatorUtilities.addClasses(into, BridgingInjectionResolver.class);
into.getService(BridgingInjectionResolver.class).setRemoteServiceLocator(from);
}
private static void checkParentage(ServiceLocator a, ServiceLocator b)
{
ServiceLocator originalA = a;
while (a != null) {
if (a.getLocatorId() == b.getLocatorId()) {
throw new IllegalStateException("Locator " + originalA + " is a child of or is the same as locator " + b);
}
a = a.getParent();
}
}
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> root)
{
ActiveDescriptor<?> ad = _localServiceLocator.getInjecteeDescriptor(injectee);
if (ad != null) {
return _localServiceLocator.getService(ad, root, injectee);
}
ad = _remoteServiceLocator.getInjecteeDescriptor(injectee);
if ((ad != null) && (ad.getDescriptorVisibility() == DescriptorVisibility.LOCAL)) {
ad = null;
}
if (ad == null) {
if (injectee.isOptional()) {
return null;
}
throw new MultiException(new UnsatisfiedDependencyException(injectee));
}
return _remoteServiceLocator.getService(ad, root, injectee);
}
@Override
public boolean isConstructorParameterIndicator()
{
return false;
}
@Override
public boolean isMethodParameterIndicator()
{
return false;
}
private void setRemoteServiceLocator(ServiceLocator remoteServiceLocator)
{
_remoteServiceLocator = remoteServiceLocator;
}
}
ServiceLocator 桥中存在错误,该错误已得到修复。修复将在 hk2 2.5.0-b07 或更高版本中进行!