Контекстно-ориентированные квалификаторы среды выполнения CDI (HK2, Guice)

Я заинтересован в использовании 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.

person akagixxer    schedule 02.02.2021    source источник
comment
В HK2 есть концепция Factorys, Это позволяет вам определить внедренный объект во время выполнения. Я мало работаю с Guice, но думаю, что их FactoryProvider аналогичен.   -  person Paul Samsotha    schedule 02.02.2021
comment
Привет @PaulSamsotha, Фабрика HK2 выглядит близко к тому, что я хотел бы. Я не уверен, как мне передать информацию о времени выполнения на фабрику, чтобы она могла выбрать правильную реализацию ИЛИ, возможно, как передать информацию о времени выполнения в инжектор для внедрения правильной фабрики.   -  person akagixxer    schedule 02.02.2021
comment
Какую информацию вы ищете?   -  person Paul Samsotha    schedule 03.02.2021
comment
Почти все, что вы можете внедрить в класс ресурсов, вы также можете внедрить в фабрику. Вы также можете внедрить ContainerRequest. Это даст вам почти все, что вам нужно о запросе.   -  person Paul Samsotha    schedule 03.02.2021
comment
Если вы используете фабрику, вы также можете внедрить InstantiationService, который можно использовать в методе предоставления, чтобы узнать, кто на самом деле пытается внедрить службу. См. javaee.github.io/hk2/apidocs/org. /стеклянная рыба/hk2/api/   -  person jwells131313    schedule 05.02.2021


Ответы (1)


В итоге я использовал функцию в HK2 под названием Операции (ссылка на документы). Это позволяет пользователю HK2 определять пользовательские области и управлять ими как операциями. Вы можете найти более подробный пример использования этой функции в проекте HK2 на github: пример работы.

Это упрощенный пример того, как я использовал эту функцию для внедрения вещей на основе контекста или, в данном случае, области действия.

Вот некоторый почти рабочий псевдокод для демонстрации моего использования:

// 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();
person akagixxer    schedule 16.02.2021
comment
Операции действительно хороши, если ваш шаблон использования подходит. Это самый простой способ создать свои собственные контексты. - person jwells131313; 22.02.2021