Google Guice 2:Mental Model
1. 序言
1.1 Guice中的键值对
- 上一篇博客,《Google Guice 1:如何实现依赖注入》,讲解了如何利用Guice实现依赖注入
- 现在,将注意力放回到
EmailModule
,它继承了AbstractModule
,重写了configure()
方法 在configure()方法中,通过
bind().to()
将EmailService
接口绑定到具体的实现类QQEmailService
public class EmailModule extends AbstractModule {
@Override
protected void configure() {
bind(EmailService.class).to(QQEmailService.class);
}
}
- 当应用程序需要EmailService类型的实例时,Guice会自动提供一个QQEmailService实例
bind().to()
这样的语法,就像定义了一个键值对,Key<EmailService>
→ \rightarrow →Provider<EmailService>
接口
Provider<T>
的定义如下,其中get()
方法负责提供一个T的实例interface Provider<T> {
/** Provides an instance of T.**/
T get();
}
Provider<EmailService>
的伪代码如下,提供一个单例的EmailService实例class EmailServiceProvider implements Provider<EmailService> {
private static final EmailService instance = new QQEmailService();
@Override
public EmailService get() {
return instance;
}
}
1.2 键值对 → \rightarrow → Guice map
这些键值对,形成了Guice map。其中,Key可以使用
Key.get()
去构建Key<EmailService> emailService = Key.get(EmailService.class);
Guice中,显式或隐式获取实例,实际都是从Guice map中获取绑定好的对象
显式获取实例:
injector.getInstance(EmailClient.class)
,显式获取EmailClient实例// 等价于如下代码
injector.getInstance(Key.get(EmailClient.class));
// getInstance()的核心伪代码如下
public EmailClient getInstance(Key<EmailClient> key) {
Provider<EmailClient> provider = guiceMap.get(key); // key为Key<EmailClient>
return provider.get();
}
隐式获取实例:显式获取EmailClient实例时,需要隐式获取EmailService实例
// 对应的核心伪代码:
Provider<EmailService> provider = guiceMap.get(Key.get(EmailService.class)); // key为Key<EmailService>
return provider.get();
2. Guice is a map
- 通过前面的学习,不难发现:Guice通过map实现对象的管理,可以说,
Guice is a map
是Guice的Mental Model
: - Mental Model:心智模型?心理模型?总之,就是Guice的建模思路
使用Guice的两个重要环节,也是围绕map展开的
- Configuration:在Guice module中配置Guice map,或者使用
Just-In-Time
bindings,自动创建对象的键值映射 - Injection:当应用程序需要时,Guice可以从map中创建并检索对象,从而实现依赖注入
- Configuration:在Guice module中配置Guice map,或者使用
- Just-In-Time bindings,简称JIT bindings,又叫
implicit bindings
,隐式绑定
2.1 Configuration
Guice module的官方解释:
A Guice module is a unit of configuration logic that adds things into the Guice map。
Guice module是一个配置逻辑单元,可以将事物(键值对,又叫binding)添加到Guice map中- 在Guice module中,可以使用Guice DSL(Domain Specific Language ),配置Guice map
主要有以下几种DSL(其中,value部分使用了lambda表达式):
Guice DSL syntax Mental model 描述 bind(key).toInstance(value)
map.put(key, () -> value)
instance binding,实例对象与key的绑定 bind(key).to(anotherKey)
map.put(key, map.get(anotherKey))
linked binding, 绑定关系就像链条,环环相扣 @Provides Foo provideFoo() {…}
map.put(Key.get(Foo.class), module::provideFoo)
provider method binding,使用 @Provides
标识一个方法,并在方法中定义对象的创建逻辑bind(key).toProvider(provider)
map.put(key, provider)
provider binding,当provider method binding变得越来越复杂时,可以考虑创建一个独立的Provider类去定义对象的创建逻辑 《Google Guice 1:如何实现依赖注入》中,使用的是instance binding,
EmailService
-->() -> qqEmailService
bind(EmailService.class).to(QQEmailService.class);
2.2 Injection
- 对象的创建依赖其他对象,你无需主动从Guice map中获取这些对象(dependency),而是直接声明
you need something
,Guice则会自动注入这些对象 Guice中,声明当前类或方法依赖其他对象的方式如下:
使用
@Inject
注解标识构造函数、字段、setter方法class Foo {
private Database database;
@Inject
Foo(Database database) {
// We need a database, from somewhere
this.database = database;
}
}
@Provides
标识的方法,为其传入参数@Provides
Database provideDatabase(@DatabasePath String databasePath) {
// 在创建Databas前,需要注入 @DatabasePath 标识的String实例
return new Database(databasePath);
}
- PS:从上面的例子可知,依赖的注入不止发生在dependent class中,在Guice module中也存在依赖注入
3. 总结
Guice实现依赖注入的几个关键点:
- 首先,以键值对的形式定义对象的创建逻辑,放入到Guice map中
- 其次,通过
@Inject
或@Provides
标识方法的入参,来声明此处需要依赖注入 - 最后,获取某个需要依赖注入的实例对象(dependent obeject)时,Guice Injector根据依赖关系,从Guice map中获取对象(dependency)并注入,从而完成dependent obeject的创建与初始化
- 整个过程中,Guice is a map,存储了对象的创建逻辑,是整个Guice的
Mental Model
还没有评论,来说两句吧...