Google Guice范例解说之使用入门

简介:

http://code.google.com/p/google-guice

Google公司的Bob lee开发的轻量级IoC容器,其特点是: 

1、速度快,号称是spring的100倍速度 
2、无配置文件,实用JDK5.0的annotation描述组件依赖,简单,而且有编译器检查和重构支持 
3、简单,代码量很少 

http://code.google.com/p/google-guice/wiki/SpringComparison 

这是Google guice和spring IoC容器的对比 

另外xwork2.0已经集成了Google guice容器了。 

http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8 

这是Google guice的快速入门文档,用起来挺简单的。

Google Guice范例解说之使用入门
本文通过范例简单地介绍Google Guice的使用,通过下面的范例我们可以知道,Google Guice的使用非常简单。

  Google Guice需要使用JDK1.5以上java环境。

  下载Google Guice之后,

  有以下几个文件:

  aopalliance.jar

  guice-1.0.jar

  guice-servlet-1.0.jar

  guice-spring-1.0.jar

  guice-struts2-plugin-1.0.jar

  本例只使用到guice-1.0.jar文件,将其加入到class path中。

  下面简单地介绍范例:

  范例1:使用com.google.inject.Module接口实现类


文件名 说明

文件名 说明
HelloGuice.java 业务逻辑接口定义文件
HelloGuiceImpl.java 业务逻辑接口实现文件
HelloGuiceModule.java 该文件必须实现com.google.inject.Module接口
TestGuice.java 测试文件


 


HelloGuice.java

package com.test.guice;

public interface HelloGuice {
public void sayHello();
}

HelloGuiceImpl.java

package com.test.guice;

public class HelloGuiceImpl implements HelloGuice {

public void sayHello() {
System.out.println("Hello Guice!");
}
}

HelloGuiceModule.java

package com.test.guice;

import com.google.inject.Binder;
import com.google.inject.Module;

public class HelloGuiceModule implements Module {

public void configure(Binder binder) {
binder.bind(HelloGuice.class).to(HelloGuiceImpl.class);
}

}

TestGuice.java
package com.test.guice;

import junit.framework.TestCase;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class TestGuice extends TestCase {
public void testHelloGuice() {
Injector injector = Guice.createInjector(new HelloGuiceModule());

HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
helloGuice.sayHello();
}
}

  运行TestGuice,打印出:

  Hello Guice!

  范例2:使用Java Annotation

  我们也可以直接为HelloGuice加上@ImplementedBy注释,而省略掉对com.google.inject.Module的实现。

HelloGuice.java
package com.test.guice;

import com.google.inject.ImplementedBy;

@ImplementedBy(HelloGuiceImpl.class)
public interface HelloGuice {
public void sayHello();
}

TestGuice.java 
package com.test.guice;

import junit.framework.TestCase;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class TestGuice extends TestCase {
public void testHelloGuice() {
//Injector injector = Guice.createInjector(new HelloGuiceModule());

Injector injector = Guice.createInjector();
HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
helloGuice.sayHello();
}
}

  HelloGuiceModule.java不再需要。其余的文件内容不变。

  运行TestGuice,打印出:

  Hello Guice!

 

 

摘自 :http://hi.baidu.com/changzhiwin/blog/item/1e8251861feb553466096e36.html 

 

 

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

附:【前沿】本教程的依赖注入部分基于老菜鸟叮咚的教程,原文在此http://www.family168.com/tutorial/guice/html/。原文主要基于Google Guice 1.0版本的,本文基于Google Guice 2.0版本进行学习和讨论。

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 

好了既然是入门教程,我们就不讨论更深层次的东西了。

转自 http://www.blogjava.net/xylz/archive/2009/xylz/archive/2009/12/23/307092.html





本文转自茄子_2008博客园博客,原文链接:http://www.cnblogs.com/xd502djj/archive/2012/06/25/2561414.html,如需转载请自行联系原作者。


目录
相关文章
|
数据挖掘 API 网络安全
如何使用Google Compute Engine入门指南快速创建和配置您的云虚拟机实例
Google Compute Engine(GCE)是 Google Cloud Platform(GCP)的一部分,它为用户提供了高度可扩展的虚拟机实例,用于运行各种工作负载,从简单的网站托管到复杂的数据分析。本文将引导您完成使用 Google Compute Engine 的入门过程,以便您可以轻松开始使用这一强大的云计算平台。
980 0
Google Bard的暂时从入门到放弃
Google Bard的暂时从入门到放弃
139 0
Google Bard的暂时从入门到放弃
|
机器学习/深度学习 Web App开发 传感器
GEE(Google Earth Engine)——JavaScript 入门(1)
GEE(Google Earth Engine)——JavaScript 入门(1)
744 0
GEE(Google Earth Engine)——JavaScript 入门(1)
|
机器学习/深度学习 存储 知识图谱
GNN入门必看!Google Research教你如何从毛坯开始搭建sota 图神经网络(下)
图神经网络近几年的发展十分火热,主要原因还是图能够表示连通关系,例如知识图谱等更贴切现实应用!Google Research最近发了一篇博客,从零开始教学GNN的发展路程,不熟悉的同学可以查缺补漏啦!
212 0
GNN入门必看!Google Research教你如何从毛坯开始搭建sota 图神经网络(下)
|
机器学习/深度学习 存储 自然语言处理
GNN入门必看!Google Research教你如何从毛坯开始搭建sota 图神经网络(上)
图神经网络近几年的发展十分火热,主要原因还是图能够表示连通关系,例如知识图谱等更贴切现实应用!Google Research最近发了一篇博客,从零开始教学GNN的发展路程,不熟悉的同学可以查缺补漏啦!
280 0
GNN入门必看!Google Research教你如何从毛坯开始搭建sota 图神经网络(上)
|
XML 开发框架 前端开发
3分钟带你了解:轻量级依赖注入框架Google Guice【享学Java】(下)
3分钟带你了解:轻量级依赖注入框架Google Guice【享学Java】(下)
|
druid Java 程序员
3分钟带你了解:轻量级依赖注入框架Google Guice【享学Java】(上)
3分钟带你了解:轻量级依赖注入框架Google Guice【享学Java】(上)
|
JavaScript 前端开发
GEE(Google Earth Engine)——JavaScript 入门(3)
GEE(Google Earth Engine)——JavaScript 入门(3)
203 0
|
存储 JavaScript 前端开发
GEE(Google Earth Engine)——JavaScript 入门(2)
GEE(Google Earth Engine)——JavaScript 入门(2)
272 0