Bindings

原文链接:https://github.com/google/guice/wiki/Bindings

Bindings

Injector的工作就是建立object graph。你请求一个给定类型的实例,然后Guice自行去构建实例、解析依赖,然后将所有绑定在一起。为了了解依赖是怎么被解析的,请使用binding来配置你的injector。

Creating Bindings

继承AbstractModule,重写configure方法,在方法主体中,调用bind()来声明每个binding。这些方法都是类型检查的所以在你使用错误的类型时编译器会告知你哪里出错了。modules创建好之后,把它们作为参数传递给Guice.createInjector()来构造一个injector。

binding包括 linked bindings, instance bindings, @Provides methods, provider bindings, constructor bindings and untargetted bindings.

Linked Binding

Linked Binding映射了类型及其实现。下面的栗子将TransactionLog接口和DatabaseTransactionLog实现类映射到一起。

public class BillingModule extends AbstractModule {
  @Override 
  protected void configure() {
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);
  }
}

现在,当你调用injector.getInstance(TransactionLog.class),或者injector需要一个TransactionLog的时候,它都会使用DatabaseTransactionLog。还可以将类绑定到它的子类上。

bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);

Linked Binding也可以串联起来:

public class BillingModule extends AbstractModule {
  @Override 
  protected void configure() {
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
  }
}

在这种情况下,当请求TransactionLog时,Guice会返回一个MySQLDatabaseTransactionLog。

Binding Annotations

有的时候你可能想要对一个类型有多个绑定(binding),比如,你可能同时想要一个PayPal credit processor和一个Google Checkout processor.为了实现这个需求,bindings支持一个可选的binding annotation。这个注解和类型一起定义了一个binding。这个组合叫做一个key。
定义一个binding annotation需要两行额外代码,以及新导入几个包。

package example.pizza;

import com.google.inject.BindingAnnotation;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface PayPal {}
  • @BindingAnnotation告诉Guice这是一个binding注解,当多个注解指向同一个成员时,Guice会提示这是一个error。
  • @Target({FIELD, PARAMETER, METHOD})是用户友好的,它可以防止@PayPal意外地被应用到无法使用的地方。
  • @Retention(RUNTIME)使得这个注解可以在运行时被获取。

To depend on the annotated binding, apply the annotation to the injected parameter:

public class RealBillingService implements BillingService {

  @Inject
  public RealBillingService(@PayPal CreditCardProcessor processor,
      TransactionLog transactionLog) {
    ...
  }

最后,我们创建一个使用注释的绑定。它在bind()语句中使用可选的annotatedWith子句:

 bind(CreditCardProcessor.class)
        .annotatedWith(PayPal.class)
        .to(PayPalCreditCardProcessor.class);
Named

Guice有一个内置的binding annotation@Named,它将一个字符串作为参数。

public class RealBillingService implements BillingService {

  @Inject
  public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
      TransactionLog transactionLog) {
    ...
  }

为了绑定一个特定的名称,使用Names.named()来生成一个实例传递给annotatedWith

    bind(CreditCardProcessor.class)
        .annotatedWith(Names.named("Checkout"))
        .to(CheckoutCreditCardProcessor.class);

由于编译器无法检查字符串,我们建议使用@Named。定义您自己的专用注释可以提供更好的类型安全。

Binding Annotations with Attributes

Guice支持具有属性值的绑定注释(如@Named)。在极少数情况下,您需要这样的注释(并且不能使用@Provides方法),我们建议您从Auto / Value项目中使用@AutoAnnotation,因为正确实现注释是很难的。如果您决定手动创建自定义实现,请务必正确实现Annotation Javadoc中详细说明的equals()hashCode()规范。将此类的实例传递给annotatedWith()绑定子句。

Instance Bindings

你可以把一个类型绑定到它的一个具体的实例上,这通常仅适用于没有自己的依赖关系的对象,例如值对象:

 bind(String.class)
        .annotatedWith(Names.named("JDBC URL"))
        .toInstance("jdbc:mysql://localhost/pizza");
    bind(Integer.class)
        .annotatedWith(Names.named("login timeout seconds"))
        .toInstance(10);

避免在复杂的对象上使用.toInstance,因为它可能会减慢应用程序启动。您可以使用@Provides方法。

Provides Methods

当需要创建对象的代码时,请使用@Provides方法。该方法必须在模块中定义,并且必须具有@Provides注解。方法的返回类型是绑定类型。每当injector需要该类型的实例时,它将调用该方法。

public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    ...
  }

  @Provides
  TransactionLog provideTransactionLog() {
    DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
    transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza");
    transactionLog.setThreadPoolSize(30);
    return transactionLog;
  }
}

如果@Provides方法具有像@PayPal@Named("Checkout")的绑定注释,则Guice绑定注释类型(Guice binds the annotated type)。依赖关系可以作为参数传递给方法。在调用该方法之前,injector将对每一个依赖执行绑定。

  @Provides @PayPal
  CreditCardProcessor providePayPalCreditCardProcessor(
      @Named("PayPal API key") String apiKey) {
    PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor();
    processor.setApiKey(apiKey);
    return processor;
  }
Throwing Exceptions

Guice不允许从Providers抛出异常。 @Provides方法抛出的异常将被包装在一个ProvisionException中。允许任何类型的异常从@Provides方法抛出--运行时或检查时--是不好的做法。如果由于某种原因需要抛出异常,可能需要使用ThrowingProviders extension ```@CheckedProvides方法。

Untargeted Bindings

您可以创建绑定而不指定目标。这对于由@ImplementedBy@ProvidedBy注解的实体类和类型是最有用的。Untargeted Binding通知injector有关类型,因此它可以热切地准备依赖关系。(An untargetted binding informs the injector about a type, so it may prepare dependencies eagerly. )Untargeted Binding没有to子句,如下所示:

 bind(MyConcreteClass.class);
 bind(AnotherConcreteClass.class).in(Singleton.class);

当指定binding annotations时,您仍然必须添加目标绑定,即使它是同一个具体的类。例如:

    bind(MyConcreteClass.class)
        .annotatedWith(Names.named("foo"))
        .to(MyConcreteClass.class);
    bind(AnotherConcreteClass.class)
        .annotatedWith(Names.named("foo"))
        .to(AnotherConcreteClass.class)
        .in(Singleton.class);

Constructor Bindings

在Guice3.0中的新特性。
有的时候需要将一个类型绑定到任意一个构造函数。这种情形在@Inject注解不能应用于目标构造函数时出现,可能是因为它是个第三方类,或者多个构造函数参与了DI。@Provides方法为这个问题提供最好的解决方案!通过明确调用目标构造函数,您不需要反思及其相关的陷阱(you don't need reflection and its associated pitfalls.)。但是这种方法有局限性:手动构造的实例不参与AOP。

为了解决这个问题,Guice有toConstructor()绑定。它们要求你慎重地选择您的目标构造函数并在找不到该构造函数时处理该异常

public class BillingModule extends AbstractModule {
  @Override 
  protected void configure() {
    try {
      bind(TransactionLog.class).toConstructor(
          DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }
}

在这个栗子里,DatabaseTransactionLog必须有一个以单独的DatabaseConnection作为参数的构造函数,该构造函数不需要@Inject注释, Guice会自行调用该构造函数来满足绑定。

每个toConstructor()绑定的范围是独立的。如果创建多个单例绑定来定位相同的构造函数,则每个绑定都会生成自己的实例。

Built-in Bindings

有一些bindings只有injector自己可以生成,尝试自己去绑定它们是一个错误的决定。

Loggers

Guice对java.util.logging.Logger有一个内置绑定,旨在保存一些模板。这个绑定自动将Logger的名称设置为注入Logger的类的名称。

@Singleton
public class ConsoleTransactionLog implements TransactionLog {

  private final Logger logger;

  @Inject
  public ConsoleTransactionLog(Logger logger) {
    this.logger = logger;
  }

  public void logConnectException(UnreachableException e) {
    /* the message is logged to the "ConsoleTransacitonLog" logger */
    logger.warning("Connect exception failed, " + e.getMessage());
  }
The Injector

在框架代码中,有时直到运行时你才知道你需要的类型。在这种罕见的情况下,you should inject the injector. Code that injects the injector does not self-document its dependencies, so this approach should be done sparingly.

Providers

对于Guice知道的每个类型,它也可以给该类型注入一个Provider。详见Injecting Providers.

TypeLiterals

Guice对于注入的所有内容都有完整的类型信息。如果您正在注入参数化类型,则可以注入TypeLiteral <T>以反映性地告诉您元素类型。

The Stage

Guice supports a stage enum to differentiate between development and production runs.

MembersInjectors

当绑定到Providers或编写扩展名时,您可能希望Guice将依赖项注入到您自己构建的对象中。为此,在MembersInjector <T>(其中T是对象的类型)中添加依赖关系,然后调用membersInjector.injectMembers(myNewObject)。(没看懂)

Just-in-time Bindings

Guice自动生成的bindings。当injector需要一个类型的实例时,它需要绑定。模块中的绑定称为显式绑定,并且injector在可用时使用它们。如果需要类型但没有明确的绑定,则injector将尝试创建just-in-time bindings。这些也称为JIT绑定和隐式绑定。

Eligible Constructors

Guice可以通过使用该类型的可注入构造函数为具体类型创建绑定。这可以是非私有的,无参数的构造函数,也可以是带有@Inject注释的构造函数:

public class PayPalCreditCardProcessor implements CreditCardProcessor {
  private final String apiKey;

  @Inject
  public PayPalCreditCardProcessor(@Named("PayPal API key") String apiKey) {
    this.apiKey = apiKey;
  }
@ImplementedBy

这个注解告诉injector该接口的默认实现类是哪个。@ImplementedBy注解表现得就像一个Linked Binding。

@ImplementedBy(PayPalCreditCardProcessor.class)
public interface CreditCardProcessor {
  ChargeResult charge(String amount, CreditCard creditCard)
      throws UnreachableException;
}

上面的注解和下面的bind()等效:

bind(CreditCardProcessor.class).to(PayPalCreditCardProcessor.class);```
如果一个类型同时包含一个```bind()```语句并且具有```@ImplementedBy```注释,则使用```bind()```语句。
#####@ProvidedBy

@ProvidedBy(DatabaseTransactionLogProvider.class)
public interface TransactionLog {
void logConnectException(UnreachableException e);
void logChargeResult(ChargeResult result);
}

与下面的```toProvider()```等效

bind(TransactionLog.class)
.toProvider(DatabaseTransactionLogProvider.class);

和```@ImplementedBy```一样,```bind()```优先级最高.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容