java - 如何使 HK2 ServiceLocator 使用桥接的 ServiceLocator 中的 Singleton 服务实例?

标签 java hk2

我们使用 ExtrasUtilities.bridgeServiceLocator() 通过将应用程序 ServiceLocator 桥接到 Jersey ServiceLocator,将在一个 ServiceLocator 中创建的现有单例应用程序服务注入(inject) Jersey RESTful Web 服务。

但是,“外部”定位器中存在的单例并未被使用 - 每个服务在注入(inject) Jersey 服务时都会再次创建。看起来单例只在 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 实现了解决方法。这不是一个完整的解决方案,因为它仅适用于注入(inject)(即对 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;
  }

}

关于java - 如何使 HK2 ServiceLocator 使用桥接的 ServiceLocator 中的 Singleton 服务实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31293388/

相关文章:

java - 我需要将标志放入同步块(synchronized block)中吗?

java - 使用 Jersey 2 的 REST API

jakarta-ee - 如何在 JUnit 测试中使用 HK2 注入(inject) CDI 事件?

java - Jersey HK2 依赖性错误

jpa - Jersey + HK2 + 灰熊 : Proper way to inject EntityManager?

java - 委托(delegate)其参数的构造函数

java - 为什么 <?扩展接口(interface)> 而不是 <?实现接口(interface)>

java - 使用相同的变量打印收据,因此每个项目都会传递相同的值。

java - CDI 和池化