1.3 更多话题
1.3.1 接口多实现
如果一个接口有多个实现,这样通过@Inject和Module都难以直接实现,但是这种现象确实是存在的,于是Guice提供了其它注入方式来解决此问题。比如下面的自定义注解。
1 public interface Service { 2 3 void execute(); 4 } 5 6 1 public class HomeService implements Service { 2 @Override 3 public void execute() { 4 System.out.println("home.imxylz.cn"); 5 } 6 } 1 public class WwwService implements Service { 2 @Override 3 public void execute() { 4 System.out.println("www.teny32.blog.51cto.com"); 5 } 6 } 1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({FIELD,PARAMETER}) 3 @BindingAnnotation 4 public @interface Home { 5 } 1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({FIELD,PARAMETER}) 3 @BindingAnnotation 4 public @interface Www { 5 }
上面的代码描述的是一个Service服务,有WwwService和HomeService两个实现,同时有Www和Home两个注解(如果对注解各个参数不明白的需要单独去学习JAVA 5注解)。好了下面请出我们的主角。
1 /** 2 * $Id: MultiInterfaceServiceDemo.java 3 * Teny study project (www.teny32.blog.51cto.com) 4 */ 5 package cn.imxylz.study.guice.inject.more; 6 7 import com.google.inject.Binder; 8 import com.google.inject.Guice; 9 import com.google.inject.Inject; 10 import com.google.inject.Module; 11 12 /** a demo with multi interfaces 13 * @author 14 * @version 1.0 15 */ 16 public class MultiInterfaceServiceDemo { 17 @Inject 18 @Www 19 private Service wwwService; 20 @Inject 21 @Home 22 private Service homeService; 23 public static void main(String[] args) { 24 MultiInterfaceServiceDemo misd = Guice.createInjector(new Module() { 25 @Override 26 public void configure(Binder binder) { 27 binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class); 28 binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class); 29 } 30 }).getInstance(MultiInterfaceServiceDemo.class); 31 misd.homeService.execute(); 32 misd.wwwService.execute(); 33 } 34 } 35 36
此类的结构是注入两个Service服务,其中wwwService是注入@Www注解关联的WwwService服务,而homeService是注入@Home注解关联的HomeService服务。
同样关于此结构我们要问几个问题。
问题(1)静态注入多个服务怎么写?
其实,参照教程02,我们可以使用下面的例子。
1 public class StaticMultiInterfaceServiceDemo { 2 @Inject 3 @Www 4 private static Service wwwService; 5 @Inject 6 @Home 7 private static Service homeService; 8 public static void main(String[] args) { 9 Guice.createInjector(new Module() { 10 @Override 11 public void configure(Binder binder) { 12 binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class); 13 binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class); 14 binder.requestStaticInjection(StaticMultiInterfaceServiceDemo.class); 15 } 16 }); 17 StaticMultiInterfaceServiceDemo.homeService.execute(); 18 StaticMultiInterfaceServiceDemo.wwwService.execute(); 19 } 20 }
问题(2):如果不小心一个属性绑定了多个接口怎么办?
非常不幸,你将得到类似一下的错误,也就是说不可以绑定多个服务。
1) cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService has more than one annotation annotated with @BindingAnnotation: cn.imxylz.study.guice.inject.more.Www and cn.imxylz.study.guice.inject.more.Home at cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService(StaticMultiInterfaceServiceDemo.java:17)
问题(3):我太懒了不想写注解来区分多个服务,怎么办?
程序员都是懒惰的,于是Google帮我们提供了一个Names的模板来生成注解。看下面的例子。
1 public class NoAnnotationMultiInterfaceServiceDemo { 2 @Inject 3 @Named("Www") 4 private static Service wwwService; 5 @Inject 6 @Named("Home") 7 private static Service homeService; 8 public static void main(String[] args) { 9 Guice.createInjector(new Module() { 10 @Override 11 public void configure(Binder binder) { 12 binder.bind(Service.class).annotatedWith(Names.named("Www")).to(WwwService.class); 13 binder.bind(Service.class).annotatedWith(Names.named("Home")).to(HomeService.class); 14 binder.requestStaticInjection(NoAnnotationMultiInterfaceServiceDemo.class); 15 } 16 }); 17 NoAnnotationMultiInterfaceServiceDemo.homeService.execute(); 18 NoAnnotationMultiInterfaceServiceDemo.wwwService.execute(); 19 } 20 }
上面的例子中我们使用Named来标注我们的服务应该使用什么样的注解,当然前提是我们已经将相应的服务与注解关联起来了。
1.3.2 Provider注入
在教程第一篇中我们提到了可以通过Provider注入一个服务,这里详细说说这种模式。
首先我们需要构造一个Provider<T>出来。
1 public class WwwServiceProvider implements Provider<Service> { 2 3 @Override 4 public Service get() { 5 return new WwwService(); 6 } 7 } 8 9
上面的Provider的意思很简单,每次新建一个新的WwwService对象出来。
注入的过程看下面的代码。
1 public class ProviderServiceDemo { 2 3 @Inject 4 private Service service; 5 6 public static void main(String[] args) { 7 Injector inj= Guice.createInjector(new Module() { 8 @Override 9 public void configure(Binder binder) { 10 binder.bind(Service.class).toProvider(WwwServiceProvider.class); 11 } 12 }); 13 ProviderServiceDemo psd = inj.getInstance(ProviderServiceDemo.class); 14 psd.service.execute(); 15 } 16 17 } 18 19
很显然如果这东西和线程绑定就非常好了,比如我们可以使用ThreadLocal来做线程的对象交换。
当然如果想自动注入(不使用Module手动关联)服务的话,可以使用@ProviderBy注解。
1 @ProvidedBy(WwwServiceProvider.class) 2 public interface Service { 3 4 void execute(); 5 } 6 7
这样我们就不必使用Module将Provider绑定到Service上,获取服务就很简单了。
ProviderServiceDemo psd = Guice.createInjector().getInstance(ProviderServiceDemo.class); psd.service.execute();
除了上述两种方式我们还可以注入Provider,而不是注入服务,比如下面的例子例子中,属性不再是Service,而是一个Provider<Service>。
1 public class ProviderServiceDemo { 2 3 @Inject 4 private Provider<Service> provider; 5 6 public static void main(String[] args) { 7 ProviderServiceDemo psd = Guice.createInjector(new Module() { 8 @Override 9 public void configure(Binder binder) { 10 binder.bind(Service.class).toProvider(WwwServiceProvider.class); 11 } 12 }).getInstance(ProviderServiceDemo.class); 13 psd.provider.get().execute(); 14 } 15 } 16 17
当然了,由于我们WwwServiceProvider每次都是构造一个新的服务出来,因此在类ProviderServiceDemo中的provider每次获取的服务也是不一样的。
1.3.3 绑定常量
看看下面的例子,演示了一个绑定整数值到实例的例子。
1 public class ConstantInjectDemo { 2 3 @Inject 4 @Named("v") 5 private int v; 6 public static void main(String[] args) { 7 8 ConstantInjectDemo cid = Guice.createInjector(new Module() { 9 @Override 10 public void configure(Binder binder) { 11 binder.bindConstant().annotatedWith(Names.named("v")).to(12); 12 } 13 }).getInstance(ConstantInjectDemo.class); 14 System.out.println(cid.v); 15 } 16 } 17 18
当然,既然可以使用Named,也就可以使用自己写注解了。但是看起来好像没有多大作用。除了上述写法,也可以用下面的方式实现。
binder.bind(int.class).annotatedWith(Names.named("v")).toInstance(12);
除了可以绑定int外,在ConstantBindingBuilder类中还可以绑定其它的基本类型。
com.google.inject.binder.ConstantBindingBuilder.to(String) com.google.inject.binder.ConstantBindingBuilder.to(long) com.google.inject.binder.ConstantBindingBuilder.to(boolean) com.google.inject.binder.ConstantBindingBuilder.to(double) com.google.inject.binder.ConstantBindingBuilder.to(float) com.google.inject.binder.ConstantBindingBuilder.to(short) com.google.inject.binder.ConstantBindingBuilder.to(char)
1.3.4 绑定Properties
除了可以绑定基本类型外,还可以绑定一个Properties到Guice中,当然了,由于Properties本质上时一个Map<String,String>,因此Guice也允许绑定一个Map<String,String>。
1 @Inject 2 @Named("web") 3 private String web; 4 5 public static void main(String[] args) { 6 7 ConstantInjectDemo cid = Guice.createInjector(new Module() { 8 @Override 9 public void configure(Binder binder) { 10 Properties properties= new Properties(); 11 properties.setProperty("web", "www.teny32.blog.51cto.com"); 12 Names.bindProperties(binder, properties); 13 } 14 }).getInstance(ConstantInjectDemo.class); 15 System.out.println(cid.web); 16 } 17 18
更多学习资料请关注本人51CTO博客文章!http://teny32.blog.51cto.com/ !