通过Guice学习理解依赖注入_第1页
通过Guice学习理解依赖注入_第2页
通过Guice学习理解依赖注入_第3页
通过Guice学习理解依赖注入_第4页
通过Guice学习理解依赖注入_第5页
已阅读5页,还剩1页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

通过Guice学习理解依赖注入提及依赖注入(DependencyInjection),想必童鞋们会想到MartinFowler的《InversionofControlContainersandtheDependencyInjectionpattern》,而本文正是通过Guice来学习理解依赖注入的实现。其次笔者在学习依赖注入之初(未曾学习《InversionofControlContainersandtheDependencyInjectionpattern》时),亦曾受"控制反转”(InversionofControl)和"依赖注入"(DependencyInjection)二概念所蒙蔽。因此为打消各位童鞋学习顾虑,此处给出明确说明,在MartinFowler未成《InversionofControlContainersandtheDependencyInjectionpattern》一文前,开源社区几位轻量级容器作者称其为"控制反转”(InversionofControl,简称IoC),但由于该名称太泛,常常会让人迷惑,因此MartinFowler和多位IoC爱好者讨论后,决定将这个模式叫做"依赖注入"(DependencyInjection)。这里可以简单理解为"控制反转”和"依赖注入”只是称谓的不同罢了。依赖示例MartinFowler在《InversionofControlContainersandtheDependencyInjectionpattern》一文中提供了—提供一份电影清单组件的例子,伪代码清单1如下:/*finder服务接口*/publicinterfaceMovieFinder(ListfindAll();}/*finder服务的消费者*/publicclassMovieLister(publicMovie[]moviesDirectedBy(Stringarg)(ListallMovies=finder.findAll();for(Iteratorit=allMovies.iterator();it.hasNext();){Moviemovie=(Movie)it.next();if(!movie.getDirector().equals(arg))it.remove();}return(Movie[])allMovies.toArray(newMovie[allMovies.size()]);}/*指向具体服务类型的实体对象*/privateMovieFinderfinder;publicMovieLister(){finder=newColonDelimitedMovieFinder("movies1.txt");}}清单1从清单1的伪代码中我们可以看出在涉及到实际寻找影片时,MovieLister类就会涉及到MovieFinder的某个具体子Finder(电影查找器)类,MovieLister类会在构造子中直接创建MovieFinder的实例,因此MovieLister类和MovieFinder会存在如图1所示的依赖关系:—nlnterfacenMovieLlster MovjeF|nder■、'-.creates" !、AMovieFinderlmp^图1图1中MovieLister类既依赖于MovieFinder接口,又依赖于MovieFinder接口的具体实现类(ColonDelimitedMovieFinder),这就会使得电影清单组件会因为在不同的应用场景中它所依赖的电影查找器可能是千差万别的。所以我们希望MovieLister类只依赖于接口,因此我们就需要有一种机制能同时满足如下两个要求:解除电影清单中对MovieFinder类型的编译时依赖;在运行时为电影清单提供所需的MovieFinder类型实例。换句话说就是在运行时建立电影清单组件中对MovieFinder所需的依赖关系,从而使其电影清单组件在编译时对MovieFinder的依赖推迟到运行时。依赖关系在面向对象的程序中是广泛存在的,只要A类型中用到了B类型实例,那么A就依赖于B。依赖示例谈到的内容是把概念抽象到了服务消费者和服务提供者的角度,这也符合现在SOA的设计思路。从另一种抽象方式上来看,可以把电影清单看成我们要构建的主系统,而MovieFinder是系统中的plugin,主系统并不强依赖于任何一个插件,但一旦插件被加载,主系统就应该可以准确调用适当插件的功能。其实不管是面向服务的编程模式,还是基于插件的框架式编程,为了实现松耦合(服务消费者和提供者之间的或者框架和插件之间的),都需要在必要的位置实现面向接口编程,在此基础之上,还应该有一种可行的机制实现具体类型之间的运行时绑定,这就是依赖注入(DependencyInjection)所要解决的问题。依赖注入的几种形式依赖注入(DependencyInjection)模式的基本思想是用一个单独的对象(装配器)来获得MovieFinder的一个合适的实现,并将其实例赋给MovieLister类的一个字段。这样一来,我们就得到了图2所示的依赖图:■=lnterface»MovieFinder图2:引入依赖注入器之后的依赖关系依赖注入的形式主要有三种,分别将它们叫做构造子注入(ConstructorInjection)、设值方法注入(SetterInjection)和接口注入(InterfaceInjection)。下面将使用GoogleGuice(后面文章中都以Guice简写)来演示构造子、设值方法注入方式:首先请允许我简单的介绍下Guice,它是2007年由两位软件大师BobLee和KevinBourrillion基于Java5开发的一个轻量级的开放源代码的依赖注入框架。它非常小而且很快,它是类型安全的,它能够对构造函数、属性和方法进行注入。使用Guice进行基本注入时首先需要告诉Guice注入点(也就是给需要注入的类添加@Inject注解),其次告诉Guice注入的类需要的依赖项,最后是使用Injector启动应用程序。构造子注入使用Guice构造子注入方式将MovieFinder实例注入MovieLister类,MovieLister类必须声明一个构造子,并在其构造子上添加@Inject注解,伪代码清单2如下:publicclassMovieLister...@InjectpublicMovieLister(MovieFinderfinder)(this.finder=finder;}清单2现在Guice知道MovieLister需要一个MovieFider,当然它还需要知道为其提供一个什么样的MovieFinder。代码清单3包含一个Module,这是一个特殊的类,用于告诉Guice各个接口对应的实现。伪代码清单3如下:publicclassMovieModuleimplementsModule(@Overridepublicvoidconfigure(Binderbinder)(binder.bind(MovieFinder.class).to(ColonDelimitedMovieFinder.class);}}清单3Guice中模块就是一个具有某种单实例对象方法的接口,Guice传递给模块的Binder用于告诉Guice您想如何构造对象。每次调用bind都会创建一个绑定,Guice将使用绑定集解析注入请求。现在使用Injector类启动Guice,代码清单4如下:publicclasstestMovie(){publicstaticvoidmain(Stringargs){Injectorinjector=Guice.createInjector(newMovieModule());MovieFinderfinder=injector.getInstance(ColonDelimitedMovieFinder.class);MovieListermovieLister=newMovieLister(finder);movieLister.moviesDirectedBy("inception");}}清单4代码清单4中为了获取注入器,需要在Guice类上调用createInjector,向createInjector传递一个模块列表,用于配置它本身。拥有注入器后,使用getInstance向它请求对象,传递您想返回的.class。设值方法注入Guice对MovieLister进行设值方法注入(Spring框架的忠实用户可以将此方法视为“setter注入”)时,需要先为它定义一个设值方法,该方法接受类型为MovieFinder的参数,伪代码清单5如下:publicclassMovieLister{privateMovieFinderfinder;@InjectpublicvoidsetMovieFinder(MovieFinderfinder){this.finder=finder;}}清单5从伪代码清单5可以看出Guice设值方法注入其实也是给设值方法添加@Inject注解。类似的Guice需要知道MovieLister需要一个MovieFider,当然它还需要知道为其提供一个什么样的MovieFinder。这一步和使用Guice进行构造子注入完全一样,可参考清单3.现在使用Injector类启动Guice,代码清单6如下:publicclasstestMovie(){publicstaticvoidmain(Stringargs){Injectorinjector=Guice.createInjector(newMovieModule());MovieFinderfinder=injector.getInstance(ColonDelimitedMovieFinder.class);MovieListermovieLister=newMovieLister();movieLister.setMovieFinder(finder);movieLister.moviesDirectedBy("inception");}}清单6接口注入除了构造子、方法两种注入技术,还可以在接口中定义需要注入的信息,并通过接口完成注入。首先需要定义一个接口,组件的注入将通过这个接口进行。在本示例中,这个接口的用途是将一个MovieFider实例注入继承了该接口的对象。接口定义代码清单7如下:publicinterfaceInjectFinder{voidinjectFinder(MovieFinderfinder);清单7接下来这个接口应该由提供MovieFider接口的人一并提供。因此任何想要使用MovieFider实例的类(例如MovieLister类)都必须实现这个接口。伪代码清单8如下:publicclassMovieListerimplementsInjectFinderprivateMovieFinderfinder;publicvoidinjectFinder(MovieFinderfinder)(this.finder=finder;}}清单8然后使用类似的方法将文件名注入MovieFider的实现类,伪代码清单9如下:publicinterfaceInjectFilename(voidinjectFilename(Stringfilename);}publicclassColonDelimitedMovieFinderimplementsMovieFinder,InjectFilename(privateStringfilename;publicvoidinjectFilename(Stringfilename)(this.filename=filename;}@OverridepublicListfindAll()(returnnull;}}清单9现在,还需要用一些配置代码将所有的组件实现装配起来。这里将配置好的MovieLister对象保存在名为lister的字段中,伪代码清单10如下:publicclassIfaceTester(privateMovieListerlister;privatevoidconfigureLister()(ColonDelimitedMovieFinderfinder=newColonDelimitedMovieFinder();finder.injectFilename("movies1.txt");lister=newMovieLister();lister.injectFinder(finder);}/*测试方法*/publicvoidtestIface()(configureLister();Movie[]movies=lister.moviesDirectedBy("SergioLeone");assertEquals("OnceUponaTimeintheWest",movies[0].getTitle());}}清单10从接口注入的实现不难看出,容器所要做的就是根据接口定义调用其中的Inject方法完成注入过程,总的原理和构造子、设值方法注入区别不大,只是在表现形式上有些不同;其次考虑到Guice未提供接口注入支持,因此本文无法直接只用Guice来实现接口注入,这里是通过Avalon架构来实现此种接口注入,因此如有兴趣的童鞋可以从/dist/avalon/framework/获取相关的资料学习。至此依赖注入的三种基本形式基本介绍完毕,同时从依赖注入的实现上不难看出,依赖注入它消除了MovieLister类对具体MovieFider实现类的依赖。这样一来,就可以把MovieLister作为一个服务提供者独立组件提供出来,让服务消费者根据自己的环境插入一个合适的MovieFider实现即可。后记:文中只使用到了Guice3种基本依赖注入方式中的构造子注入和方法注入,其实Guice还支持field注入。其次在Guice的依赖注入中,Guice只关心@Inject注解,因此基于方法和field的依赖注入可以是基于私有方法、field的注入(但不推荐对私有field进行注入,这样会影响可测试性)。而更

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论