Bindings

2017-06-21  本文已影响0人  2fc2a81494ac

原文链接: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 {}

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()```优先级最高.
上一篇下一篇

猜你喜欢

热点阅读