在第2章中我们可以通过接口注入,构造方法注入,setter方法注入方式去声明依赖关系,Ioc Service Provider 是用来将Ioc场景中的业务对象绑定到一起的实现方式。
它是一个抽象的概念,可以用代码实现,也可以是一组相关的类,同是还能是Ioc框架或者通用的Ioc容器。Ioc Service Provider 是用来提供对象间的依赖对象的,而Spring中的Ioc Service Provider 则是Spring的自带的Ioc容器,提供了依赖注入服务。
我们不经要问那么Ioc Service Provider 的具体的职责是什么?Ioc Service Provider 是怎么实现提供服务,怎么去管理对象之间的依赖关系呢?
- Ioc Service Provider 的具体的职责
1.依赖对象的构建管理:
比如我们被注入对象是ServiceImpl通过setter注入的方式可以直接获取到依赖对象IDao,建立依赖关系。而此时的IDao业务对象并不是由ServiceImple来构建的。Ioc Service Provider 的职责之一就是依赖对象的构建管理工作。将依赖对象的构建逻辑从客户端隔离,接触了相应的耦合。
2. 业务对象间的依赖绑定
业务对象之间的依赖绑定也是Ioc Service Provider 的最重要的功能,它的最终使命。若是没有实现业务对象间的依赖绑定,无论业务对象怎么去呼唤去通知,也不会得到依赖对象的相应的响应,若是没有实现业务之间的依赖绑定,比如SeriviceImple需要依赖对象IDao,但是没有相应的依赖绑定,没有谁提供服务给你。好比去酒吧喝酒,你说你要啤酒但是服务员不在或者服务员压根不知道啤酒是什么,酒虽然有了,但是没有谁能够去提供给你喝。Ioc Service Provider 通过结合之前的构建和管理所有的业务对象,以及各个业务对象之间可以识别的依赖关系,将这些对象所依赖的对象注入绑定,从而保证每个业务对象在使用被注入对象时候,被注入的对象处于就绪状态。
那么问题来了,Ioc Service Provider 到底是怎么管理对象之间的依赖关系,怎么实现的呢?
在第2章的笔记中有记录,被注入对象可以通过3种注入方式去通知Ioc Service Provider 为其注入相应的依赖。但是问题是是否Ioc Service Provider 就能一定并且正确的注入呢?当时是不。
好比我们去酒吧喝酒(比作被注入对象),此时服务员在(比作Ioc), 我们想要啤酒(依赖注入对象)。但是服务员此刻是个新人,并不知道啤酒放在哪里,你若要想喝啤酒,服务员必须知道啤酒和库存的关系。对于Ioc Service Provider 来说的话,同样的需要知道自己所管理和掌握的被注入对象与依赖注入对象之前的对应关系,才能准确无误的去为被注入对象提供注入服务, 那么Ioc Service Provider 就需要寻求一些能够记录被注入对象与依赖对象之间的关系。可以通过最基本的文本文件记录,或者通过描述性强的XML文档方式,或者语音或者图片。
实际上,当前流行的Ioc Service Provider 使用的依赖绑定信息管理有以下方式
- 直接编码方式:
IocContainer container = ....container.register(ServiceImple.class,new ServiceImple());container.register(IDao1.class,new Dao1());container.register(IDao2.class,new Dao2());....ServiceImpl serImpl = (ServiceImple)container.get(ServiceImple.class);....
通过为相应的类指定具体的实例,可以告知Ioc容器,当我们需要这种类型对象时候,请将容器中注册的、对应的具体的实例给予我们。
若是通过接口注入方式注入的话,除了需要以上的的伪代码,还需要对于ServiceImple的需要实现的接口进行绑定相应的对象,才能让容器知道具体的依赖关系。
IocContainer container = ....container.register(ServiceImple.class,new ServiceImple());container.register(IDao1.class,new Dao1());container.register(IDao2.class,new Dao2());Container.bind(IServiceImple,IDao1.class);Container.bind(IServiceImple,IDao2.class);....ServiceImpl serImpl = (ServiceImple)container.get(ServiceImple.class);...
通过bind()方法,将被注入对象(通过实现接口方式)与依赖对象IDao1,IDao2进行绑定,容器在返回ServiceImple对象实例之前,根据绑定信息,将 IDao1,IDao2注册到容器中的对象实例注入到被注入对象ServiceImpel中,并返回已经组装好的ServiceImple。
所以通过直接编码可以让Ioc Service Provider 得以管理业务对象之间的依赖绑定方式,这也是一种最基本的方式
- xml等配置文件方式
< bean id = "dao1" class = "...dao.impl.Dao1 " ></ bean >
< bean id = "dao2" class = "...dao.impl.Dao2 " ></ bean >
< bean id = "serviceImpl" class = "...service.impl.ServiceImple " >< property name = "dao1" ref = "dao1" />< property name = "dao2" ref = "dao2" /></ bean >
当配置文件配置完成后,如下伪代码为怎样从容器中获取到被注入对象并使用:
...container.readConfigurationFiles( ...);ServiceImpl serviceImpl = (ServiceImpl)Container.getbean( "serviceImpl" );...
- 元数据方式
private
IDao1
dao1
;
private
IDao2
dao2
;
public
IDao1 getDao1() {
return
dao1
;
}
@Inject
public
void
setDao1(IDao1
dao1
) {
this
.
dao1
=
dao1
;
}
@Inject
public
IDao2 getDao2() {
return
dao2
;
}
public
void
setDao2(IDao2
dao2
) {
this
.
dao2
=
dao2
;
}
这里通过Inject,指明需要Ioc Service Provider 通过setter方式去注入,为ServiceImpl注入所依赖的对象。至于剩下的相关的信息,由相应的Module来提供,如下给出了ServiceImpl所使用的Module。
public
class
ServiceBindingModule
extends
AbstractModule{
@Override
protected
void
configure(){
bind(IDao1 .
class
),to( Dao1.
class
).in(Scope.SINGLETON);
bind(IDao2 .
class
).to(Dao1.
class
).in(Scope.SINGLETON )
}
}
通过Module指定进一步的依赖注入相关信息后,我们就可以直接从Guice那里取得最终的已经注入,并可以使用的对象
Injector
injector
= Guice.createInjector(
new
ServiceBindingModule());
ServiceImpl
serviceImpl
=
injector
.getInstance(ServiceImpl.
class
);
...