Guice 中的绑定(bindings)是 Guice Key
和 value 实例的一个对应关系,即 Guice Map
中的一项(entry)。不管是通过方法注解和是 DSL
,将 Guice Key
和实例进行关联便创建了一个绑定。
Guice 中存在多种绑定关系。
Linked Bindings
Linked Bindings
是将接口类型和接口的实现的类型关联起来的绑定。
Binding Annotations
在 https://blog.kicey.site/guice-review-map/ 中提到可以将类型和注解组合起来作为一个 Guice Key
,这里的注解需要符合 Guice 的规范,即是一个 Binding Annotations
,注解的声明上需要注解上 @Qualifier
或者 @BindingAnnotation
来表明这是这个注解可以用来和类型组合作为 Guice Key
(注解还需要在运行时可用,如果自定义的注解带有属性,那么还需要实现注解的 equals()
和 hashCode()
方法)。
为了简化 Binding Anotations
的使用,Guice 提供了 @Name()
注解,使用如下:
声明需要注入的实例:
public RealBillingService implements BillingService {
@Inject
public RealBillingService(@Named("Checkout") CreditCardProcessor processor, TransactionLog transactionLog){
...
}
}
关联 Guice Key
和实例:
final class CreditCardProcessorModule extends AbstractModule {
@Override
protected void configure() {
bind(CreditCardProcessor.class)
.annotateWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
}
}
关联 Guice Key
和实例时也可以使用:
final class CreditCardProcessorModule extends AbstractModule {
@Provides
@Named("Checkout")
CreditCardProcessor provideCheckoutCreditCardProcessor() {
return new CheckoutCreditCardProcessor();
}
}
Instance Bindings
通常我们定义 Guice 中的一对 key/value 时,并不会直接指定一个实例作为 value,而是指定实例的具体类型,和一个返回实例的方法,让 Guice 来创建这个实例。我们也可以直接指定实例来创建这个 key/value 关系,例如:
...
bind(String.class)
.annotatedWith(Names.named("JDBC URL"))
.toInstance("jdbc:mysql://localhost/pizza");
...
@Provides Methods
在 Module 中定义 @Provides
方法时,将会把方法注解和返回类型作为 Guice Key
,将实际的返回实例作为 value,并且自动的创建一个绑定。
Provider Bindings
除了在 Module 中定义 @Provides
的方法外,我们也可以直接定义一个实现了 Provider<T>
接口的类。二者的作用是相同的。不过直接定义接口的方式需要多一个使用 DSL
进行绑定的步骤,如下:
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class)
.toProvider(DatabaseTransactionLogProvider.class);
}
}
Untargeted Bindings
在定义一个 Binding
时只提供类型作为 Guice Key
,而具体的实例由类型上的 @ImplementedBy
或者 @ProvideBy
注解值指定。
Constructor Bindings
偶尔我们需要将第三方的类型加入 Guice 的管理,这时我们没法改动第三方库中的类型的源码(没法加入 @Inject
注解),这时候我们可以使用 DSL
中的 toConstructor()
将实例和第三方类型的构造方法绑定,例如:
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).toConstructor(
DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
}
Built-in Bindings
Guice 不需要任何配置就在启动时包含了部分绑定(类似于 spring-boot-starter 会在 spring 的容器中自动的创建一些 bean)。
Loggers
,每次注入新创建一个实例,将被注入的类名作为Loggers
的名称Injector
,Guice 中的注解器,可以直接获取依赖,类似于 spring 的BeanFactory
Providers
,所有自动和手动创建的Providers
TypeLiterals
,如果你注入了泛型参数,可以同时注入TypeLiterals
来获取具体的参数类型Stage
,表明当前是开发或生产环境MembersInjectors
,进行字段注入使用的实例
Just-in-time Bindings
Guice 不仅能使用显示定义的绑定,也可以在自动根据需要注入的类型创建绑定(隐式绑定),这种自动创建的绑定对实例的构造方法有一定的要求,需要满足下面条件中的一个:
- 实例的构造方法以
@Inject
注解了 - 实例具有无参的,并且是
public
的构造方法
Guice 可以通过配置只允许创建第一种类型的绑定:
binder().requireAtInjectRequired()
在 Guice 3.0 以后使用
binder().requireExplicitBindins()
Multibindings
多绑定:一个 Guice Key
绑定一个实例集合 ,用于插件式的框架结构中。多绑定依靠 3 个类: Multibinder
, MapBinder
, OptionalBinder
。
例如以下代码将 UriSummarizer
和它的多个实现绑定。
public class FlickerPluginModule extends AbstractModule {
public void configure() {
Multibinder<UriSummarizer> uriBinder = MultiBinder.newSetBinder(binder(), UriSummarizer.class);
userBinder.addBinding().to(FlickPhotoSummarizer.class);
userBinder.addBinding().to(FakeUriSummarizer.class);
}
}
注入构造方法时以集合的形式注入 Set<UriSummarizer>
。
值得注意的是 MultiBinder.newSetBinder(binder, type)
这个方法并不是为一个 Guice Key
对应类型创建一个新的集合,而是会在原有的集合上进行操作。
默认情况下实例集合是不运行出现重复的具体类型的实例的,不过可以使用 multiBinder.permitDuplicates()
解除这个限制。
除了将多个实例聚合成集合外,还可以将实例聚合成一个 Map,例如 Map<String, UrlSummarizer>
,相应的,创建绑定的 DSL
有一些变化,如下:
public class FlickerPluginModule extends AbstractModule {
public void configure() {
Multibinder<UriSummarizer> uriBinder = MapBinder.newMapBinder(binder(), UriSummarizer.class);
userBinder.addBinding("flick").to(FlickPhotoSummarizer.class);
userBinder.addBinding("fake).to(FakeUriSummarizer.class);
}
}
注入时使用 Map<String, UriSummarizer>
。
和 Set 的情况一样,Map 也存在重复绑定的问题,不同的时 Map 的重复限制在 Map 的 key 值上,同样可以使用 binder.permitDuplicates()
解除限制。
在上面的两个样例代码中,第一份代码创建了一个 MultiBinder
,第二个则使用了 MapBinder
。
OptionalBinder
如果在构造方法中使用形如 Optional<MyInterface>
的类型,那么 Guice 将创建相应的 OptionalBinder
。可以为这个 OptionalBinder
设置默认值,如下:
public class FrameworkLoggingModule extends AbstractModule {
protected void configure() {
OptionalBinder.newOptionalBinder(binder(), RequestLogger.class)
.setDefault()
.to(DefaultRequestLoggerImpl.class);
}
}
在指定默认值之后只能使用 setBinding
覆写默认值,例如:
public class ConsoleLoggingModule extends AbstractModule {
@Override
protected void configure() {
OptionalBinder.newOptionalBinder(binder(), RequestLogger.class)
.setBinding()
.to(ConsoleLogger.class);
}
}
@Provides-like
在使用多绑定的情况下,除了通过 DSL
显示的绑定之外,还可以使用 @ProvidesIntoSet
, @ProvidesIntoMap
, ProvidesIntoOptional
注解方法的形式进行。
@ProvidesIntoSet
public class FlickrPluginModule extends AbstractModule {
@ProvidesIntoSet
UriSummarizer provideFlickerUriSummarizer() {
return new FlickrPhotoSummarizer(...);
}
}
@ProvidesIntoMap
public class FlickrPluginModule extends AbstractModule {
@StringMapKey("Flickr")
@ProvidesIntoMap
UriSummarizer provideFlickrUriSummarizer() {
return new FlickrPhotoSummarizer(...);
}
}
除了使用 String
作为 key 之外,还可以使用注解和注解的 value()
返回值(以 unwrapValue 的布尔值做区分)作为 key。
@MapKey(unwrapValue=true)
@Retention(RUNTIME)
public @interface MyCustomEnumKey {
MyCustomEnum value();
}
@ProvidersIntoOptional
public class FrameworkModule extends AbstractModule {
@ProvidesIntoOptional(ProvidesIntoOptional.Type.DEFAULT)
@Singleton
RequestLogger provideConsoleLogger() {
return new DefaultRequestLoggerImpl();
}
}
public class RequestLoggingModule extends AbstractModule {
@ProvidesIntoOptional(ProvidesIntoOptional.Type.ACTUAL)
@Singleton
RequestLogger provideConsoleLogger() {
return new ConsoleLogger(System.out);
}
}