01 - 依赖注入
1. 依赖注入
1.1 类依赖注入
所谓的绑定就是将一个接口绑定到具体的类中,这样客户端不用关心具体的实现,而只须要获取相应的接口完成其服务便可。
HelloWorld.java
1 public interface HelloWorld {
2
3 String sayHello();
4 }
5
而后是具体的实现,HelloWorldImpl.java
1 public class HelloWorldImpl implements HelloWorld {
2
3 @Override
4 public String sayHello() {
5 return "Hello, world!";
6 }
7 }
8
写一个测试例子看看,HelleWorldTest.java
1 public class HelleWorldTest {
2
3 @Test
4 public void testSayHello() {
5 Injector inj= Guice.createInjector(new Module() {
6 @Override
7 public void configure(Binder binder) {
8 binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
9 }
10 });
11 HelloWorld hw = inj.getInstance(HelloWorld.class);
12 Assert.assertEquals(hw.sayHello(), "Hello, world!");
13 }
14 }
15
这个例子很是简单,通俗的将就是将一个HelloWorldImpl的实例与HelloWorld关联起来,当想Guice获取一个 HelloWorld实例的时候,Guice就返回一个HelloWorldImpl的实例,而后咱们就能够调用HelloWorld服务的方法了。
问题(1)HelloWorld是单例的么?测试下。
1 HelloWorld hw = inj.getInstance(HelloWorld.class);
2 Assert.assertEquals(hw.sayHello(), "Hello, world!");
3 HelloWorld hw2 = inj.getInstance(HelloWorld.class);
4 System.out.println(hw.hashCode()+"->"+hw2.hashCode());
5 Assert.assertEquals(hw.hashCode(), hw2.hashCode());
解答(1)测试结果告诉咱们,HelloWorld不是单例的,每次都会返回一个新的实例。
问题(2)HelloWorld的实例是HelloWorldImpl么?能够强制转型么?
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.getClass().getName());
解答(2),结果输出cn.imxylz.study.guice.helloworld.HelloWorldImpl,看来确实只是返回了一个正常的实例,并无作过多的转换和代理。
问题(3),若是绑定多个实现到同一个接口上会出现什么状况?
1 public class HelloWorldImplAgain implements HelloWorld {
2 @Override
3 public String sayHello() {
4 return "Hello world again.";
5 }
6 }
binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class);
解答(3),很不幸,Guice目前看起来不容许多个实例绑定到同一个接口上了。
com.google.inject.CreationException: Guice creation errors:
1) A binding to cn.imxylz.study.guice.helloworld.HelloWorld was already configured at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28).
at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:29)
问题(4),能够绑定一个实现类到实现类么?
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorldImpl.class).to(HelloWorldImpl.class);
5 }
6 });
7 HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
8 System.out.println(hw.sayHello());
很是不幸,不能够本身绑定到本身。
1) Binding points to itself.
at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28)
咱们来看看bind的语法。
<T> AnnotatedBindingBuilder<T> bind(Class<T> type);
ScopedBindingBuilder to(Class<? extends T> implementation);
也就是说只能绑定一个类的子类到其自己。改造下,改用子类替代。
1 public class HelloWorldSubImpl extends HelloWorldImpl {
2
3 @Override
4 public String sayHello() {
5 return "@HelloWorldSubImpl";
6 }
7 }
8
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorldImpl.class).to(HelloWorldSubImpl.class);
5 }
6 });
7 HelloWorldImpl hw = inj.getInstance(HelloWorldImpl.class);
8 System.out.println(hw.sayHello());
太好了,支持子类绑定,这样即便咱们将一个实现类发布出去了(尽管不推荐这么作),咱们在后期仍然有办法替换实现类。
使用bind有一个好处,因为JAVA 5以上的泛型在编译器就肯定了,因此能够帮咱们检测出绑定错误的问题,而这个在配置文件中是没法检测出来的。
这样看起来Module像是一个Map,根据一个Key获取其Value,很是简单的逻辑。
问题(5),能够绑定到咱们本身构造出来的实例么?
解答(5)固然能够!看下面的例子。
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorld.class).toInstance(new HelloWorldImpl());
5 }
6 });
7 HelloWorld hw = inj.getInstance(HelloWorld.class);
8 System.out.println(hw.sayHello());
问题(6),我不想本身提供逻辑来构造一个对象能够么?
解答(6),能够Guice提供了一个方式(Provider<T>),容许本身提供构造对象的方式。
1 Injector inj= Guice.createInjector(new Module() {
2 @Override
3 public void configure(Binder binder) {
4 binder.bind(HelloWorld.class).toProvider(new Provider<HelloWorld>() {
5 @Override
6 public HelloWorld get() {
7 return new HelloWorldImpl();
8 }
9 });
10 }
11 });
12 HelloWorld hw = inj.getInstance(HelloWorld.class);
13 System.out.println(hw.sayHello());
问题(7),实现类能够不通过绑定就获取么?好比我想获取HelloWorldImpl的实例而不经过Module绑定么?
解答(7),能够,实际上Guice可以自动寻找实现类。
Injector inj= Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
System.out.println(hw.sayHello());
问题(8),可使用注解方式完成注入么?不想手动关联实现类。
解答(8),好,Guice提供了注解的方式完成关联。咱们须要在接口上指明此接口被哪一个实现类关联了。
1 @ImplementedBy(HelloWorldImpl.class)
2 public interface HelloWorld {
3
4 String sayHello();
5 }
6
Injector inj= Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.sayHello());
事实上对于一个已经被注解的接口咱们仍然可使用Module来关联,这样获取的实例将是Module关联的实例,而不是@ImplementedBy注解关联的实例。这样仍然遵循一个原则,手动优于自动。
问题(9)再回头看问题(1)怎么绑定一个单例?
1 Injector inj = Guice.createInjector(new Module() {
2
3 @Override
4 public void configure(Binder binder) {
5 binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class).in(Scopes.SINGLETON);
6 }
7 });
8 HelloWorld hw = inj.getInstance(HelloWorld.class);
9 HelloWorld hw2 = inj.getInstance(HelloWorld.class);
10 System.out.println(hw.hashCode() + "->" + hw2.hashCode());
11
能够看到如今获取的实例已是单例的,再也不每次请求生成一个新的实例。事实上Guice提供两种 Scope,com.google.inject.Scopes.SINGLETON和 com.google.inject.Scopes.NO_SCOPE,所谓没有scope便是每次生成一个新的实例。
对于自动注入就很是简单了,只须要在实现类加一个Singleton注解便可。
1 @Singleton
2 public class HelloWorldImpl implements HelloWorld {
3
4 @Override
5 public String sayHello() {
6 return "Hello, world!";
7 }
8 }
9java
Google Guice 入门教程02 - 依赖注入
1.2 属性注入(Field Inject)
1.2.1 基本属性注入
首先来看一个例子。Service.java
1 @ImplementedBy(ServiceImpl.class)
2 public interface Service {
3 void execute();
4 }
ServiceImpl.java
1 public class ServiceImpl implements Service {
2 @Override
3 public void execute() {
4 System.out.println("This is made by imxylz (www.imxylz.cn).");
5 }
6 }
FieldInjectDemo.java
1 /** a demo with Field inject
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 71 $
4 */
5 public class FieldInjectDemo {
6 @Inject
7 private Service servcie;
8 public Service getServcie() {
9 return servcie;
10 }
11 public static void main(String[] args) {
12 FieldInjectDemo demo = Guice.createInjector().getInstance(FieldInjectDemo.class);
13 demo.getServcie().execute();
14 }
15 }
这个例子比较简单。具体来讲就是将接口Service经过@Inject注解注入到FieldInjectDemo类中,而后再 FieldInjectDemo类中使用此服务而已。固然Service服务已经经过@ImplementedBy注解关联到ServiceImpl 类中,每次生成一个新的实例(非单例)。注意,这里FieldInjectDemo类没有经过Module等关联到Guice中,具体能够查看《》。
意料之中获得了咱们期待的结果。
一样,咱们经过问答的方式来加深理解(注意,入门教程咱们只是强调怎么使用,至于原理和底层的思想咱们放到高级教程中再谈)。
问题(1):能够本身构造FieldInjectDemo 对象而不经过Guice么?
1 /** field inject demo2
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 73 $
4 */
5 public class FieldInjectDemo2 {
6 @Inject
7 private Service servcie;
8 public Service getServcie() {
9 return servcie;
10 }
11 public static void main(String[] args) {
12 FieldInjectDemo2 fd = new FieldInjectDemo2();
13 fd.getServcie().execute();
14 }
15 }
就像上面的例子中同样,而后运行下看看?很是不幸,咱们获得了一个谁都不喜欢的结果。
Exception in thread "main" java.lang.NullPointerException
at cn.imxylz.study.guice.inject.FieldInjectDemo2.main(FieldInjectDemo2.java:22)
很显然,因为FieldInjectDemo2不属于Guice容器(暂且称为容器吧)托管,这样Service服务没有机会被注入到FieldInjectDemo2类中。
问题(2):能够注入静态属性么?
看下面的代码。
1 public class FieldInjectDemo2 {
2 @Inject
3 private static Service servcie;
4 public static Service getServcie() {
5 return servcie;
6 }
7 public static void main(String[] args) {
8 FieldInjectDemo2 fd = Guice.createInjector().getInstance(FieldInjectDemo2.class);
9 FieldInjectDemo2.getServcie().execute();
10 }
11 }
很不幸!运行结果告诉咱们Guice看起来还不支持静态字段注入。
好了,上面两个问题咱们暂且放下,咱们继续学习其它注入功能。
1.2.2 构造函数注入(Constructor Inject)
继续看例子。例子是说明问题的很好方式。
1 /**
2 * $Id: ConstructorInjectDemo.java 75 2009-12-23 14:22:35Z xylz $
3 * xylz study project (www.imxylz.cn)
4 */
5 package cn.imxylz.study.guice.inject;
6
7 import com.google.inject.Guice;
8 import com.google.inject.Inject;
9
10 /** a demo with constructor inject
11 * @author xylz (www.imxylz.cn)
12 * @version $Rev: 75 $
13 */
14 public class ConstructorInjectDemo {
15
16 private Service service;
17 @Inject
18 public ConstructorInjectDemo(Service service) {
19 this.service=service;
20 }
21 public Service getService() {
22 return service;
23 }
24 public static void main(String[] args) {
25 ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
26 cid.getService().execute();
27 }
28
29 }
30
31
咱们在构造函数上添加@Inject来达到自动注入的目的。构造函数注入的好处是能够保证只有一个地方来完成属性注入,这样能够确保在构造函数中完成一些初始化工做(尽管不推荐这么作)。固然构造函数注入的缺点是类的实例化与参数绑定了,限制了实例化类的方式。
问题(3):构造函数中能够自动注入多个参数么?
1 public class ConstructorInjectDemo {
2
3 private Service service;
4 private HelloWorld helloWorld;
5 @Inject
6 public ConstructorInjectDemo(Service service,HelloWorld helloWorld) {
7 this.service=service;
8 this.helloWorld=helloWorld;
9 }
10 public Service getService() {
11 return service;
12 }
13 public HelloWorld getHelloWorld() {
14 return helloWorld;
15 }
16 public static void main(String[] args) {
17 ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
18 cid.getService().execute();
19 System.out.println(cid.getHelloWorld().sayHello());
20 }
21 }
22
23
很是完美的支持了多参数构造函数注入。固然了没有必要写多个@Inject,并且写了的话不能经过编译。
1.2.3 Setter注入(Setter Method Inject)
有了上面的基础咱们再来看Setter注入就很是简单了,只不过在setter方法上增长一个@Inject注解而已。
1 public class SetterInjectDemo {
2
3 private Service service;
4
5 @Inject
6 public void setService(Service service) {
7 this.service = service;
8 }
9
10 public Service getService() {
11 return service;
12 }
13
14 public static void main(String[] args) {
15 SetterInjectDemo sid = Guice.createInjector().getInstance(SetterInjectDemo.class);
16 sid.getService().execute();
17 }
18
19 }
20
21
好了咱们再回头看问题2的静态注入(static inject)。下面的例子演示了如何注入一个静态的字段。
1 /** a demo for static field inject
2 * @author xylz (www.imxylz.cn)
3 * @version $Rev: 78 $
4 */
5 public class StaticFieldInjectDemo {
6
7 @Inject
8 private static Service service;
9
10 public static void main(String[] args) {
11 Guice.createInjector(new Module() {
12 @Override
13 public void configure(Binder binder) {
14 binder.requestStaticInjection(StaticFieldInjectDemo.class);
15 }
16 });
17 StaticFieldInjectDemo.service.execute();
18 }
19 }
20
21
很是棒!上面咱们并无使用Guice获取一个StaticFieldInjectDemo实例(废话),实际上static字段(属性)是类相关的,因 此咱们须要请求静态注入服务。可是一个好处是在外面看起来咱们的服务没有Guice绑定,甚至client不知道(或者不关心)服务的注入过程。
再回到问题(1),参考上面静态注入的过程,咱们可使用下面的方式来注入实例变量的属性。
1 public class InstanceFieldInjectDemo {
2
3 @Inject
4 private Service service;
5 public static void main(String[] args) {
6 final InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
7 Guice.createInjector(new Module() {
8 @Override
9 public void configure(Binder binder) {
10 binder.requestInjection(ifid);
11 }
12 });
13 ifid.service.execute();
14 }
15 }
16
17
实际上这里有一种简便的方法来注入字段,实际上此方法也支持Setter注入。
1 public class InstanceFieldInjectDemo {
2
3 @Inject
4 private Service service;
5 public static void main(String[] args) {
6 InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
7 Guice.createInjector().injectMembers(ifid);
8 ifid.service.execute();
9 }
10 }
11
12
好了既然是入门教程,咱们就不讨论更深层次的东西了。web
Google Guice 入门教程03 - 依赖注入
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.imxylz.cn");
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 82 2009-12-24 06:55:16Z xylz $
3 * xylz study project (www.imxylz.cn)
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 xylz (www.imxylz.cn)
14 * @version $Rev: 82 $
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.imxylz.cn");
12 Names.bindProperties(binder, properties);
13 }
14 }).getInstance(ConstantInjectDemo.class);
15 System.out.println(cid.web);
16 }
17
18ide
Google Guice 入门教程04 - 依赖注入
本章节继续讨论依赖注入的其余话题,包括做用域(scope,这里有一个与线程绑定的做用域例子)、当即初始化(Eagerly Loading Bindings)、运行阶段(Stage)、选项注入(Optional Injection)等等。
1.3.5 Scope(做用域)
在1.1章节中咱们初步了解了对象的单例模式,在Guice中提供了一些常见的做用域,好比对于单例模式有下面两个做用域。
com.google.inject.Scopes.SINGLETON
com.google.inject.Scopes.NO_SCOPE
在使用上,可使用Module的bind来实现,看下面的例子。
1 public class ScopeDemo {
2 public static void main(String[] args) {
3
4 Service service = Guice.createInjector(new Module() {
5 @Override
6 public void configure(Binder binder) {
7 binder.bind(Service.class).to(WwwService.class).in(Scopes.SINGLETON);
8 }
9 }).getInstance(Service.class);
10 service.execute();
11 }
12 }
13
14
固然单例模式还能够彷佛用@Singleton注解。
在com.google.inject.binder.ScopedBindingBuilder.in(Scope)方法中,一个Scope除了可使 上面的SINGLETION和NO_SCOPE外,还能够是本身定义的Scope。下面的例子演示了一个与线程绑定的Scope例子。
1 /**
2 * $Id: ThreadScopeDemo.java 90 2009-12-25 08:12:21Z xylz $
3 * xylz study project (www.imxylz.cn)
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.Injector;
10 import com.google.inject.Key;
11 import com.google.inject.Module;
12 import com.google.inject.Provider;
13 import com.google.inject.Scope;
14
15 /** a demo with thread-scope
16 * @author xylz (www.imxylz.cn)
17 * @version $Rev: 90 $
18 */
19 public class ThreadScopeDemo {
20
21 static class ThreadServiceScope implements Scope {
22
23 static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
24
25 @Override
26 public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
27 return new Provider<T>() {
28 @Override
29 public T get() {
30 T instance = (T) threadLocal.get();
31 if (instance == null) {
32 instance = unscoped.get();
33 threadLocal.set(instance);
34 }
35 return instance;
36 }
37 };
38 }
39
40 @Override
41 public String toString() {
42 return "Scopes.ThreadServiceScope";
43 }
44 }
45
46 public static void main(String[] args) {
47 final Injector inj=Guice.createInjector(new Module() {
48 @Override
49 public void configure(Binder binder) {
50 binder.bind(Service.class).to(WwwService.class).in(new ThreadServiceScope());
51 }
52 });
53 for(int i=0;i<3;i++) {
54 new Thread("Thread-"+i) {
55 public void run() {
56 for(int m=0;m<3;m++) {
57 System.out.println(String.format("%s-%d:%d",//
58 getName()//
59 ,m//
60 ,inj.getInstance(Service.class).hashCode()));
61 try {
62 Thread.sleep(50L);
63 } catch (Exception e) {
64 }
65 }
66 }
67 }.start();
68 }
69 }
70 }
71
注意,这里用到了《Google Guice 入门教程03 - 依赖注入》的中的两个类Service和WwwService。在本例中ThreadServiceScope 类是一个与线程绑定的做用域(利用ThreadLocal特性),当当前线程中没有构造一个对象的时候先构造一个出来,而后放入线程上下文中,之后每次都 从线程中获取对象。第50行是将WwwService服务以ThreadServiceScope的做用域绑定到Service服务上。第57-60行输 出当前对象的hashCode,若是此类是同一对象的话就应该输出相同的hashCode。为了看到效果,咱们使用3个线程,每一个线程输出三次来看结果。
Thread-0-0:18303751
Thread-1-0:23473608
Thread-2-0:21480956
Thread-1-1:23473608
Thread-0-1:18303751
Thread-2-1:21480956
Thread-1-2:23473608
Thread-2-2:21480956
Thread-0-2:18303751
咱们看到对于同一个线程(好比说Thread-0)的三次都输出了相同的对象(hashCode为18303751),而与线程2和线程3的hashCode不一样。
(特别说明:若是两个线程输出了同一个hashCode没必要惊慌,那是由于可能前一个线程生成的对象的地址空间被GC释放了,结果下一个线程使用了上一个线程的相同空间,因此这里使用Thread.sleep来下降这种可能性)
事实上在guice-servlet-2.0.jar中有与request和session绑定的scope。
com.google.inject.servlet.ServletScopes.REQUEST
com.google.inject.servlet.ServletScopes.SESSION
1.3.6 Eagerly Loading Bindings (当即初始化)
除了能够绑定scope外,对象默认在第一次调用时被建立,也即所谓的延时加载,Guice也容许对象在注入到Guice容器中时就被建立出来(显然这是针对单例模式才有效)。
1 public class EagerSingletonDemo {
2
3 public EagerSingletonDemo() {
4 System.out.println(" constuctor:"+System.nanoTime());
5 }
6 void doit() {
7 System.out.println(" doit:"+System.nanoTime());
8 }
9 public static void main(String[] args) throws Exception{
10 Injector inj = Guice.createInjector(new Module() {
11 @Override
12 public void configure(Binder binder) {
13 binder.bind(EagerSingletonDemo.class).asEagerSingleton();
14 }
15 });
16 System.out.println("before call:"+System.nanoTime());
17 Thread.sleep(100L);
18 inj.getInstance(EagerSingletonDemo.class).doit();
19 }
20 }
结果输出以下:
constuctor:26996967388652
before call:26996967713635
doit:26997069993702
能够看到咱们的对象在调用getInstance以前就已经被构造出来了。
1.3.7 Stages (运行阶段)
Guice还有一个特效,能够指定Guice运行模式来控制Guice的加载速度。在com.google.inject.Stage枚举中提供了TOOL,DEVELOPMENT,PRODUCTION三种模式。
TOOL描述的是带有IDE等插件的运行模式;DEVELOPMENT是指在开发阶段只加载本身须要的功能(对于非当即初始化单例对象采用延后加载),这 样来下降加载不须要功能的时间;而PRODUCTION模式是指彻底加载全部功能(对于单例对象采用当即加载方式),这样能够更早的发现问题,省得等须要 某些功能的时候才发现问题(要知道咱们某些功能可能须要特定的条件才能触发)。
其实只有比较多的单例对象,而且单例对象构造比较耗时的状况下才能有用。大部分状况下这点性能可能都忽略不计了。
默认状况下Guice采用DEVELOPMENT模式。
1.3.8 Optional Injection (选项注入 )
选项注入描述的是若是不能从Guice容器中注入一个对象,那么可使用一个默认的对象。看下面的例子。
1 public class OptionalInjectionDemo {
2 @Inject(optional=true)
3 Service service = new WwwService();
4 public static void main(String[] args) {
5 Guice.createInjector(new Module() {
6 public void configure(Binder binder) {
7 //binder.bind(Service.class).to(HomeService.class);
8 }
9 }).getInstance(OptionalInjectionDemo.class).service.execute();
10 }
11 }
上述例子中第2行描述的是选项注入,若是不能从Guice容器中获取一个Service服务那么就使用默认的WwwService,不然就是用获取的服务。若是将第7行注释去掉咱们就能够看到实际上调用的是HomeService服务了。性能