【问题标题】:Context Oriented Runtime CDI Qualifiers (HK2, Guice)面向上下文的运行时 CDI 限定符(HK2,Guice)
【发布时间】:2021-02-02 17:20:08
【问题描述】:

我有兴趣使用 HK2 或 Guice 作为依赖注入框架。 我知道@Named@Qualifier 和自定义注释等。但这些都是编译时的。

我正在寻找一种工具来根据运行时上下文动态确定所需的具体类型并注入正确的实现。

在 HK2 或 Guice 中是否有类似的东西或实现此目标的推荐方式?

例如:

// I would want to turn this...
public final class Handler
{ 
  private final Session session;
   
  @Inject   
  public Handler(@Named("Database") final Session session)
  {
    this.session = session;
  }
  ...
}

// into something like this...
public final class Handler
{
  private final Session session;
    
  @Inject
  public Handler(final Session session)
  {
    this.session = session;
  }
}

// where "session" is injected based on some previous context value ("Database")
// or something to that effect.

【问题讨论】:

  • HK2中有Factorys的概念,这可以让你在运行时判断注入的对象。我与 Guice 的合作不多,但我认为他们的 FactoryProvider 是类似的。
  • 嗨@PaulSamsotha,HK2 工厂看起来很接近我想要的。我不确定如何将运行时信息传递给工厂,以便它可以决定正确的实现,或者也许如何将运行时信息传递给注入器以注入正确的工厂。
  • 您在寻找什么信息?
  • 几乎任何你可以注入资源类的东西,你也可以注入工厂。您还可以注入 ContainerRequest。这将为您提供有关请求所需的几乎所有信息。
  • 如果你确实使用了工厂,你也可以注入一个可以在提供方法中使用的 InstantiationService 来发现谁实际上正在尝试注入服务。见javaee.github.io/hk2/apidocs/org/glassfish/hk2/api/…

标签: java dependency-injection cdi guice hk2


【解决方案1】:

我最终在HK2 中使用了一个名为Operations 的功能(链接到文档)。它允许 HK2 的用户定义自定义范围并将它们作为“操作”进行管理。您可以在 HK2 的 github 项目中找到更详细的如何使用该功能的示例:operations example

这是一个简化示例,说明我最终如何使用此功能根据上下文或在本例中为“scope”注入内容。

这里有一些几乎可以工作的伪代码来演示我的用法:

// Create the custom scope annotation.
@Scope
@Proxiable(proxyForSameScope = false)
@Documented
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface BatchScope
{
  public static final BatchScope INSTANCE = new BatchScopeEnvoy();
}


final class BatchScopeEnvoy extends AnnotationLiteral<BatchScope> implements BatchScope
{
  private static final long serialVersionUID = 938233179310254573L;
}


// Create a context used by the HK2 operation feature.
@Singleton
public final class BatchScopeContext extends OperationContext<BatchScope>
{
  @Override
  public Class<? extends Annotation> getScope()
  {
    return BatchScope.class;
  }
}


// Create a class that holds your custom scope data/context.
public final class BatchScopeRuntime
{
   // ... Arbitrary runtime data here ...
     
     public SomeData getData()
     {
       return this.data;
     }
}


// Create a factory that serves up something you want to inject from a custom scope.
@Singleton
public final class DataFactory implements Factory<SomeData>
{
  private final OperationManager operations;

  @Inject
  public BatchInfoFactory(final OperationManager operations)
  {
    Sentinel.assertIsNotNull(operations);

    this.operations = operations;
  }


  // The @BatchScope on the provide() method indicates that objects returned 
  // from this factory are in the "BatchScope".
  @Override
  @BatchScope
  public IBatchInfo provide()
  {
    final OperationHandle handle = this.operations.getCurrentOperation(BatchScope.INSTANCE);
    final BatchScopeRuntime runtime = (BatchScopeRuntime)handle.getOperationData();

    return runtime.getData();
  }

  @Override
  public void dispose(final IBatchInfo instance)
  {
    // Do nothing.
  }
}


// Setup the injector.
public static ServiceLocator createInjector(final String name)
{
    final ServiceLocator injector = ServiceLocatorFactory.getInstance().create(name);

    ServiceLocatorUtilities.bind(
      injector,
      new AbstractBinder()
      {
        @Override
        protected void configure()
        {
           // This creates a "Singleton" factory that provides
           // "SomeData" instances at "BatchScope".
           bindFactory(DataFactory.class, Singleton.class)
            .to(SomeData.class)
            .in(BatchScope.class);
        }
      }
        
    return injector;
}


// Create a class that needs something in the custom scope.
public final class Foo
{
  @Inject
  public Foo(final SomeData data)
  {
    System.out.printf("I got: %s%n", data);
  }
}   


// Usage: how to manage the scopes using the operations feature.
final SomeData data = ... // get some data
final BatchScopeRuntime runtime = new BatchScopeRuntime(data); // Setup the runtime information.

// Create an operation handle for the custom scope and associate the custom data with it.
final ServiceLocator injector = createInjector("test");
ServiceLocatorUtilities.addClasses(injector, BatchScopeContext.class, Foo.class);
final OperationManager operations = injector.getService(OperationManager.class);
final OperationHandle<BatchScope> batchScope = operations.createAndStartOperation(BatchScope.INSTANCE);

// Operation/scope is now associated with the current thread.           
batchScope.setOperationData(runtime);

// Foo will now be injected with: "data" from above.
final Foo foo = injector.getService(Foo.class);

// Do some work...

// Close the operation (make it go out of scope) on the current thread.
batchScope.closeOperation();

【讨论】:

  • 如果您的使用模式适合,操作会非常好。这是制作您自己的自定义上下文的最简单方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-26
  • 1970-01-01
相关资源
最近更新 更多