google guice 入门教程01 -凯发k8网页登录

关注后端架构、中间件、分布式和并发编程

   :: 凯发k8网页登录首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  111 随笔 :: 10 文章 :: 2680 评论 :: 0 trackbacks


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),允许自己提供构造对象的方式。


 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
附:【前沿】本教程的依赖注入部分基于老菜鸟叮咚的教程,原文在此。原文主要基于google guice 1.0版本的,本文基于google guice 2.0版本进行学习和讨论。

下一篇:


©2009-2014 imxylz
|求贤若渴
posted on 2009-12-22 23:28 imxylz 阅读(35513) 评论(5)     所属分类: j2eegoogle guice
# re: google guice 入门教程01 - 依赖注入 2009-12-23 09:49
不错  回复  
  

# re: google guice 入门教程01 - 依赖注入(1)[未登录] 2010-07-01 09:57
very nice!  回复  
  

# re: google guice 入门教程01 - 依赖注入(1) 2011-06-15 23:32
不错  回复  
  

# re: google guice 入门教程01 - 依赖注入(1) 2012-07-25 19:00
博主小哥,你认识张民松吗  回复  
  

# re: google guice 入门教程01 - 依赖注入(1)[未登录] 2016-03-05 14:12
guice的实现方式不太优雅,在配置的继承重用和annotation侵入性上有问题,我最近做了一个小项目叫jbeanbox (不能发链接,请google之) 。特点:1.只用单个java文件350行源码实现完整ioc/aop 2.用java类代替xml作为配置,支持ide重构,无侵入性(没用annotation),敬请评价。  回复  
  


©2009-2014
网站地图