第三章 【第二节】 绑定注解(BindingAnnotations):Guice指导教程(原创翻译)

目录
[隐藏]

绑定注解 Binding Annotations

有时,你会把多个绑定到一个类型上(一个类型可能会有多个实现类)。例如:你希望既有Paypal Pos机,同时也有Google Pos机。为了实现这个场景,绑定支持一种叫做“绑定注解”(binding annotation)的方式。一个绑定由注解和类型唯一确定(在绑定时加上注解,这样可以确定在依赖注入时用的具体是哪个实现类)。这一对叫做“键”(key)。

Occasionally you’ll want multiple bindings for a same type. For example, you might want both a PayPal credit card processor and a Google Checkout processor. To enable this, bindings support an optional binding annotation. The annotation and type together uniquely identify a binding. This pair is called a key.

具体有2种,一种是自定义注解,另一种是@Named

自定义注解

定义注解需要两行代码以及一些引入(import)的包。

Defining a binding annotation requires two lines of code plus several imports. Put this in its own .java file or inside the type that it annotates. 

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 {}

你也许不需要关心这些元注解的意义。但这里简单介绍一下:

  • @BindingAnnotationtells 告诉Guice这是一个绑定注解。如果多个绑定注解应用到相同的成员上,Guice会报错。
  • @Target({FIELD, PARAMETER, METHOD}) 它们被用来提供对其它 annotation类型作说明,使用了target可更加明晰其修饰的目标。说明了Annotation所修饰的对象范围。限定@PayPal的应用范围。
  • @Retention(RUNTIME) 使注解在运行时有效(即运行时保留)。

You don’t need to understand all of these meta-annotations, but if you’re curious:

  • @BindingAnnotation tells Guice that this is a binding
    annotation. Guice will produce an error if ever multiple binding
    annotations apply to the same member.
  • @Target({FIELD, PARAMETER, METHOD}) is a courtesy to your users. It prevents@PayPal from being accidentally being applied where it serves no purpose.
  • @Retention(RUNTIME) makes the annotation available at 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语句来实现

Lastly we create a binding that uses the annotation. This uses the optional annotatedWith clause in the bind() statement:

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

@Named注解

Guice提供使用@Named的注解,同时,它的参数需要一个字符转值。

Guice comes with a built-in binding annotation @Named that uses a string:

public class RealBillingService implements BillingService {

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

为了绑定到特定的名字上,用Names.named()标注,并同样使用annotatedWith语法。

To bind a specific name, use Names.named() to create an instance to pass to annotatedWith:

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

由于编译器无法检验这个字符串参数,所以我们建议尽量少用@Named

Since the compiler can’t check the string, we recommend using @Named sparingly.

注解绑定的属性 Binding Annotations with Attributes

Guice支持绑定注释拥有属性值。但很少情况下才会这样用:

  1. 创建注解@interface
  2. 创建一个类,并实现这个注解接口。按Annotation Javadoc里的原则实现equals()和hashCode()。通过这个实例应用到annotatedWith()绑定语句上去。

Guice supports binding annotations that have attribute values. In the rare case that you need such an annotation:

  1. Create the annotation @interface.
  2. Create a class that implements the annotation interface. Follow the guidelines for equals() and hashCode() specified in the Annotation Javadoc. Pass an instance of this to the annotatedWith() binding clause.


实例

BindingAnnotations(绑定注释):一个类型可能会有多个实现类,在绑定时加上注解,这样可以确定在依赖注入时用的具体是哪个实现类。
具体有2种,一种是自定义注解,另一种是@Named。

自定义注解

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 Tom {
 
}
 
public interface Animal {
    void say();
}
 
public class Cat implements Animal{
 
    @Override
    public void say() {
        System.out.println("i am a cat");
    }
 
}
 
public class Dog implements Animal{
 
    @Override
    public void say() {
        System.out.println("i am a dog");
    }
 
}
 
public interface HelloService {
    void sayHello();
}
 
import com.google.inject.Inject;
 
public class HelloServiceImpl implements HelloService {
 
    Animal animal;
     
    @Inject
    public HelloServiceImpl(@Tom Animal animal) {
        this.animal = animal;
    }
     
    @Override
    public void sayHello() {
        System.out.println(animal.getClass().getSimpleName());
        animal.say();
    }
}
 
import com.google.inject.AbstractModule;
 
public class HelloServiceModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Animal.class).annotatedWith(Tom.class).to(Cat.class);
        bind(Animal.class).to(Dog.class);
        bind(HelloService.class).to(HelloServiceImpl.class);
    }
 
}
 
public class Test {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new HelloServiceModule());
        HelloService helloService = injector.getInstance(HelloService.class);
        helloService.sayHello();
    }
}

执行结果:

Cat
i am a cat

当把HelloServiceImpl的构造函数里的@Tom去掉后,执行结果:
Dog
i am a dog

发现,多个实现类绑定到一个类型时,后者覆盖前者。

@Named注解:

import com.google.inject.Inject;
import com.google.inject.name.Named;
 
public class HelloServiceImpl2 implements HelloService {
 
    Animal animal;
     
    @Inject
    public HelloServiceImpl2(@Named("Snoopy") Animal animal) {
        this.animal = animal;
    }
     
    @Override
    public void sayHello() {
        System.out.println(animal.getClass().getSimpleName());
        animal.say();
    }
}
 
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
 
public class HelloServiceModule2 extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Animal.class).annotatedWith(Names.named("Snoopy")).to(Dog.class);
        bind(Animal.class).to(Cat.class);
        bind(HelloService.class).to(HelloServiceImpl2.class);
    }
 
}
 
import com.google.inject.Guice;
import com.google.inject.Injector;
 
public class Test2 {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new HelloServiceModule2());
        HelloService helloService = injector.getInstance(HelloService.class);
        helloService.sayHello();
    }
}

执行结果:

Dog
i am a dog

实例参考来源:http://lifestack.cn/archives/100.html

下一节:实例绑定 Instance Bindings


说明:

      鉴于网上guice中文资料较少,出于个人爱好,对该项目下的用户API文档进行翻译。如有翻译不恰当之处,还望指正。

      google Guice 项目地址:https://github.com/google/guice

      Guice 英文API地址:https://github.com/google/guice/wiki/LinkedBindings

分享到:

发表评论

昵称

沙发空缺中,还不快抢~