java - 面向上下文的运行时 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.
解决方案
我最终在HK2中使用了一个名为Operations的功能(链接到文档)。它允许 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();
推荐阅读
- blazor - 保护 Blazor webassembly 源
- api - 没有方法签名:io.restassured.internal.ContentParser.parse()
- python - ValueError:轴与数组 Pytorch 转置操作不匹配
- apache - 如何将 nginx.conf 文件配置为 httpd-ssl.conf 作为代理服务器
- javascript - 无法在节点中读取 null 的属性“名称”
- html - 什么决定了字母所在元素的最高和最低字母与上下边界之间的空间?
- python - 如何循环多个 .json 文件
- c++ - macOS 上的 Clang - 将 isysroot 设置为默认位置
- postgresql - Postgres FTS 优先字段
- c++ - 尝试渲染像素坐标时,简单线渲染失败