我正在寻找一种在运行时使用 google guice 动态选择正确依赖项的方法。
我的用例是一个 kotlin 应用程序,它可以根据提供的配置文件使用 sqlite 或 h2 数据库。
执行应用程序时会读取该文件,如果未找到数据库,则会创建正确的数据库并将其迁移到其中。
我的数据库结构包含Database
(接口)H2Database: Database
,SQLiteDatabase: Database
和模块绑定类,看起来像这样:
class DatabaseModule: KotlinModule() {
override fun configure() {
bind<Database>().annotatedWith<configuration.H2>().to<H2Database>()
bind<Database>().annotatedWith<configuration.SQLite>().to<SQLiteDatabase>()
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,仅使用 SQlite,我只需使用以下命令请求依赖项:
@Inject
@SQLite
private lateinit var database: Database
Run Code Online (Sandbox Code Playgroud)
我将如何在运行时进行此选择?
在不太了解您的代码的具体内容的情况下,我将提供三种通用方法。
(另外,我从来没有用过 Kotlin。我希望 Java 示例足以让你弄清楚。)
听起来您需要一些重要的逻辑来确定哪个数据库实现是正确的使用。这是ProviderBinding的经典案例。Database
您绑定Database
到一个负责提供实例的类(一个Provider),而不是绑定到特定的实现。例如,你可能有这个类:
public class MyDatabaseProvider.class implements Provider<Database> {
@Inject
public MyDatabaseProvider.class(Provider<SQLiteDatabase> sqliteProvider, Provider<H2Database> h2Provider) {
this.sqliteProvider = sqliteProvider;
this.h2Provider = h2Provider;
}
public Database get() {
// Logic to determine database type goes here
if (isUsingSqlite) {
return sqliteProvider.get();
} else if (isUsingH2) {
return h2Provider.get();
} else {
throw new ProvisionException("Could not determine correct database implementation.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
(旁注:此示例代码每次都会为您提供一个新实例。让它也返回一个单例实例是相当简单的。)
然后,要使用它,您有两个选择。在您的模块中,您不会绑定Database
到特定的实现,而是绑定到您的DatabaseProvider
. 像这样:
protected void configure() {
bind(Database.class).toProvider(MyDatabaseProvider.class);
}
Run Code Online (Sandbox Code Playgroud)
这种方法的优点是,在 Guice 尝试构造一个需要Database
作为其构造函数参数之一的对象之前,您不需要知道正确的数据库实现。
您可以创建一个DatabaseRoutingProxy
类,该类实现Database
然后委托给正确的数据库实现。(我专业地使用了这种模式。我认为这种设计模式没有“官方”名称,但您可以在此处找到讨论。)这种方法基于延迟加载,Provider
使用 Guice自动创建的 Providers ( 1) 对于每个绑定类型。
public class DatabaseRoutingProxy implements Database {
private Provider<SqliteDatabse> sqliteDatabaseProvider;
private Provider<H2Database> h2DatabaseProvider;
@Inject
public DatabaseRoutingProxy(Provider<SqliteDatabse> sqliteDatabaseProvider, Provider<H2Database> h2DatabaseProvider) {
this.sqliteDatabaseProvider = sqliteDatabaseProvider;
this.h2DatabaseProvider = h2DatabaseProvider;
}
// Not an overriden method
private Database getDatabase() {
boolean isSqlite = // ... decision logic, or maintain a decision state somewhere
// If these providers don't return singletons, then you should probably write some code
// to call the provider once and save the result for future use.
if (isSqlite) {
return sqliteDatabaseProvider.get();
} else {
return h2DatabaseProvider.get();
}
}
@Override
public QueryResult queryDatabase(QueryInput queryInput) {
return getDatabase().queryDatabase(queryInput);
}
// Implement rest of methods here, delegating as above
}
Run Code Online (Sandbox Code Playgroud)
在你的 Guice 模块中:
protected void configure() {
bind(Database.class).to(DatabaseRoutingProxy.class);
// Bind these just so that Guice knows about them. (This might not actually be necessary.)
bind(SqliteDatabase.class);
bind(H2Database.class);
}
Run Code Online (Sandbox Code Playgroud)
这种方法的优点是在您实际进行数据库调用之前,您不需要知道要使用哪个数据库实现。
这两种方法都假设您无法实例化 H2Database 或 SqliteDatabase 的实例,除非后备数据库文件确实存在。如果可以在没有后备数据库文件的情况下实例化对象,那么您的代码就会变得简单得多。(只要有一个路由器/代理/委托人/任何将实际Database
实例作为构造函数参数的东西。)
这种方法与后两种完全不同。在我看来,您的代码实际上是在处理两个问题:
如果您甚至可以在创建需要知道问题 2 答案的 guice 注入器之前解决问题 1,那么您就不需要做任何复杂的事情。你可以有一个这样的数据库模块:
public class MyDatabaseModule extends AbstractModule {
public enum DatabaseType {
SQLITE,
H2
}
private DatabaseType databaseType;
public MyDatabaseModule(DatabaseType databaseType) {
this.databaseType = databaseType;
}
protected void configure() {
if (SQLITE.equals(databaseType)) {
bind(Database.class).to(SqliteDatabase.class);
} else if (H2.equals(databaseType)) {
bind(Database.class).to(H2Database.class);
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于您已将问题 1 和 2 分开,因此当您创建将使用 的注入器时MyDatabaseModule
,您可以为构造函数参数传入适当的值。
笔记
Provider<T>
每个绑定都会存在一个T
。我在没有创建相应提供程序的情况下成功创建了绑定,因此 Guice 必须为配置的绑定自动创建一个提供程序。(编辑:我发现更多的文档更清楚地说明了这一点。) 归档时间: |
|
查看次数: |
1575 次 |
最近记录: |