绑定注解 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.
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 |
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:
1 2 3 |
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:
1 2 3 4 5 6 7 |
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:
1 2 3 |
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支持绑定注释拥有属性值。但很少情况下才会这样用:
- 创建注解@interface
- 创建一个类,并实现这个注解接口。按Annotation Javadoc里的原则实现equals()和hashCode()。通过这个实例应用到annotatedWith()绑定语句上去。
Guice supports binding annotations that have attribute values. In the rare case that you need such an annotation:
- Create the annotation @interface.
- 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。
自定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
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注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
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
发表评论
难的是和自己保持联系~