如何绑定动态@Named 绑定
How to bind a dynamic @Named binding
我想创建一个可配置的模块,它将绑定一些不同的@Named 事物。 Applications/injections 使用模块的人会提前知道@Name,但模块本身直到在运行时实例化后才会知道。
我在我的示例代码中使用了 kotlin,但很高兴得到 java 个答案。
编译失败,因为所有@Named 注释都需要引用常量字符串,而不是运行时变量(An annotation argument must be a compile-time constant
):
class DbModule(val configPath: String) : KotlinModule() {
@Provides
@Named(configPath) // <-- can't do this
fun provideDbConfig(loader: ConfigLoader): DbConfig {
// note ConfigLoader is separately bound,
// but a needed depenency of DbConfig
return DbConfig(loader, configPath)
}
@Provides
@Named(configPath) // <-- can't do this
fun provideDataSource(
@Named(configPath) // <-- can't do this
dbConfig: DbConfig): DataSource
{
return dbConfig.dataSource
}
}
我可以通过添加 Provider 使 DbConfig 绑定工作:
private class ConfigProvider
@Inject constructor(
val loader: ConfigLoader,
@Named("configPath") val configPath: String
) : Provider<DbConfig> {
override fun get(): DbConfig {
return DbConfig(loader, configPath)
}
}
class DbModule(val configPath: String) : KotlinModule() {
override configure() {
bindConstant().annotatedWith(Names.named("configPath"))
.to(configPath)
bind<DbConfig>().annotatedWith(Names.named(configPath))
.toProvider(ConfigProvider::class.java)
}
}
但我不确定如何获得 Provider<DataSource>
可以使用正确的 configPath
注释 DbConfig()
以便它可以获得 DataSource
脱离配置?我可能能够有一个 DataSourceProvider
以与 ConfigProvider
相同的方式构造自己的 DbConfig(configPath)
,但让 guice 通过 ConfigProvider
和创建 dbconfig 似乎更可取能够在 DataSourceProvider
?
中利用它
最后,我希望能够注入以下内容:
class BusinessObject1
@Inject constructor(
@Named("secondaryDb") val dbConfig: DbConfig
)
class BusinessObject2
@Inject constructor(
@Named("secondaryDb") val dataSource: DataSource
)
假设这些对象是由注入器创建的:
Guice.createInjector(DbModule("secondaryDb"))
(另请注意,上面的代码不允许同时创建 DbModule("secondaryDb")
和 DbModule("tertiaryDb")
,但这可以通过私有模块解决,我将其保留以避免额外的复杂性)
你离开了PrivateModule, but that's exactly what I'd use to solve your problem. If I've guessed your KotlinModule source correctly, it has a counterpart in KotlinPrivateModule。
Guice 文档在其常见问题解答中将此解决方案提倡为 "robot legs problem"(想象一下用相同的大腿、膝盖和小腿绑住左右腿,但左右腿不同)"How do I build two similar but slightly different trees of objects?".
在 Java 中看起来像:
public class DbModule extends PrivateModule {
private final String configPath;
public DbModule(String configPath) { this.configPath = configPath; }
// (no @Named annotation; bind it like it's the only one!)
@Provides DbConfig provideDbConfig(ConfigLoader loader) {
return new DbConfig(loader, configPath);
}
// (no @Named annotation; bind it like it's the only one!)
@Provides DataSource provideDataSource(DbConfig dbConfig) {
return dbConfig.dataSource;
}
@Override public void configure() {
// now bind the unqualified one to the qualified one
bind(DbConfig.class).annotatedWith(Names.named(configPath)).to(DbConfig.class);
bind(DataSource.class).annotatedWith(Names.named(configPath)).to(DataSource.class);
// and now you can expose only the qualified ones
expose(DbConfig.class).annotatedWith(Names.named(configPath));
expose(DataSource.class).annotatedWith(Names.named(configPath));
}
}
这样您的 @Provides
方法就不需要尝试使用仅在运行时可用的注解,并且您不会使用不合格的 DbConfig 和 DataSource 绑定使全局 Injector 混乱。此外——这是解决方案的真正好处——在 DbModule 中,您可以直接注入 DbConfig 和 DataSource,而无需 @Named
注释。这使得生产和使用可重复使用的机器变得更加容易,因为您的可重复使用的部件不会有任何 @Named
注释需要担心。您甚至可以将配置路径绑定为字符串(@Named("configPath") String
或 @ConfigPath String
)并将其直接注入 DbConfig,从而允许您使用 @Inject
标记 DbConfig 并摆脱其 @Provides
方法。
(对于它的价值,如果您使用了不使用 PrivateModules 的替代解决方案,而是使用更长更复杂的 bind
语句和 Names.named
,那么 DbModule("secondaryDb")
和 DbModule("tertiaryDb")
可以共存,只要 public 绑定不相互冲突。)
我想创建一个可配置的模块,它将绑定一些不同的@Named 事物。 Applications/injections 使用模块的人会提前知道@Name,但模块本身直到在运行时实例化后才会知道。
我在我的示例代码中使用了 kotlin,但很高兴得到 java 个答案。
编译失败,因为所有@Named 注释都需要引用常量字符串,而不是运行时变量(An annotation argument must be a compile-time constant
):
class DbModule(val configPath: String) : KotlinModule() {
@Provides
@Named(configPath) // <-- can't do this
fun provideDbConfig(loader: ConfigLoader): DbConfig {
// note ConfigLoader is separately bound,
// but a needed depenency of DbConfig
return DbConfig(loader, configPath)
}
@Provides
@Named(configPath) // <-- can't do this
fun provideDataSource(
@Named(configPath) // <-- can't do this
dbConfig: DbConfig): DataSource
{
return dbConfig.dataSource
}
}
我可以通过添加 Provider 使 DbConfig 绑定工作:
private class ConfigProvider
@Inject constructor(
val loader: ConfigLoader,
@Named("configPath") val configPath: String
) : Provider<DbConfig> {
override fun get(): DbConfig {
return DbConfig(loader, configPath)
}
}
class DbModule(val configPath: String) : KotlinModule() {
override configure() {
bindConstant().annotatedWith(Names.named("configPath"))
.to(configPath)
bind<DbConfig>().annotatedWith(Names.named(configPath))
.toProvider(ConfigProvider::class.java)
}
}
但我不确定如何获得 Provider<DataSource>
可以使用正确的 configPath
注释 DbConfig()
以便它可以获得 DataSource
脱离配置?我可能能够有一个 DataSourceProvider
以与 ConfigProvider
相同的方式构造自己的 DbConfig(configPath)
,但让 guice 通过 ConfigProvider
和创建 dbconfig 似乎更可取能够在 DataSourceProvider
?
最后,我希望能够注入以下内容:
class BusinessObject1
@Inject constructor(
@Named("secondaryDb") val dbConfig: DbConfig
)
class BusinessObject2
@Inject constructor(
@Named("secondaryDb") val dataSource: DataSource
)
假设这些对象是由注入器创建的:
Guice.createInjector(DbModule("secondaryDb"))
(另请注意,上面的代码不允许同时创建 DbModule("secondaryDb")
和 DbModule("tertiaryDb")
,但这可以通过私有模块解决,我将其保留以避免额外的复杂性)
你离开了PrivateModule, but that's exactly what I'd use to solve your problem. If I've guessed your KotlinModule source correctly, it has a counterpart in KotlinPrivateModule。
Guice 文档在其常见问题解答中将此解决方案提倡为 "robot legs problem"(想象一下用相同的大腿、膝盖和小腿绑住左右腿,但左右腿不同)"How do I build two similar but slightly different trees of objects?".
在 Java 中看起来像:
public class DbModule extends PrivateModule {
private final String configPath;
public DbModule(String configPath) { this.configPath = configPath; }
// (no @Named annotation; bind it like it's the only one!)
@Provides DbConfig provideDbConfig(ConfigLoader loader) {
return new DbConfig(loader, configPath);
}
// (no @Named annotation; bind it like it's the only one!)
@Provides DataSource provideDataSource(DbConfig dbConfig) {
return dbConfig.dataSource;
}
@Override public void configure() {
// now bind the unqualified one to the qualified one
bind(DbConfig.class).annotatedWith(Names.named(configPath)).to(DbConfig.class);
bind(DataSource.class).annotatedWith(Names.named(configPath)).to(DataSource.class);
// and now you can expose only the qualified ones
expose(DbConfig.class).annotatedWith(Names.named(configPath));
expose(DataSource.class).annotatedWith(Names.named(configPath));
}
}
这样您的 @Provides
方法就不需要尝试使用仅在运行时可用的注解,并且您不会使用不合格的 DbConfig 和 DataSource 绑定使全局 Injector 混乱。此外——这是解决方案的真正好处——在 DbModule 中,您可以直接注入 DbConfig 和 DataSource,而无需 @Named
注释。这使得生产和使用可重复使用的机器变得更加容易,因为您的可重复使用的部件不会有任何 @Named
注释需要担心。您甚至可以将配置路径绑定为字符串(@Named("configPath") String
或 @ConfigPath String
)并将其直接注入 DbConfig,从而允许您使用 @Inject
标记 DbConfig 并摆脱其 @Provides
方法。
(对于它的价值,如果您使用了不使用 PrivateModules 的替代解决方案,而是使用更长更复杂的 bind
语句和 Names.named
,那么 DbModule("secondaryDb")
和 DbModule("tertiaryDb")
可以共存,只要 public 绑定不相互冲突。)