1. 序言
1.1 Guice中的键值对
-
上一篇博客,《Google Guice 1:如何实现依赖注入》,讲解了如何利用Guice实现依赖注入
-
现在,将注意力放回到
EmailModule
,它继承了AbstractModule
,重写了configure()
方法 -
在configure()方法中,通过
bind().to()
将EmailService
接口绑定到具体的实现类QQEmailService
public class EmailModule extends AbstractModule {@Overrideprotected 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();@Overridepublic 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(EmailService.class)
,显式获取EmailService实例 - 隐式获取实例:通过
injector.getInstance(EmailClient.class)
显式获取EmailClient实例时,Guice会自动注入EmailService实例。EmailService实例,是Guice从Guice map中隐式获取的
- 显式获取实例:
-
从Guice map获取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;@InjectFoo(Database database) { // We need a database, from somewherethis.database = database;} }
- @Provides标识的方法,为其传入参数
@Provides Database provideDatabase(@DatabasePath String databasePath) { // 在创建Databas前,需要注入 @DatabasePath 标识的Stringreturn 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