??xml version="1.0" encoding="utf-8" standalone="yes"?>
――构造子注入Q?/SPAN>Constructor InjectionQ?/SPAN>模式的分析与实现
?/SPAN> ?/SPAN>
Q?/SPAN>chaocai2001@yahoo.com.cnQ?/SPAN>
摘要Q本文对IoC模式、依赖注?/SPAN>(Dependency Injection) 模式做了要介l,文中分析构造子注入模式与其他模式相比较的优势和特点Qƈl出了在JAVA中实现该模式的方法?/SPAN>
IoCQ?/SPAN>Inversion of ControlQ模式以被目前的轻量U容器所q泛应用Q通过IoC模式q些容器帮助开发者将来自不同目的组件装配成一个内聚的应用E序。轻量?/SPAN>IoC容器Q如Spring?/SPAN>pico-containerQ虽然ؓ我们的开发提供了很大的便利,但是在很多情况下q些轻量U容器所提供的功能ƈ不一定非帔R合我们的需要,也许q些容器的功能过于庞大了Q或者所提供的功能缺乏对特定应用的针Ҏ,或者我们需要更高的q行效率Q这时我们可以在了解IoC的原理的基础上利?/SPAN>JAVA的反机制自己实现灵zȝ、可扩展的组件机制?/SPAN>
?/SPAN>GoF的设计模式相同,IoC模式同样是关注重用性,但与GoF模式不同的是IoC模式更加x二进制的重用性和可扩展性,卛_以直接通过二进制q行扩充Q复用的模块通常被称为组件或者插Ӟlg和插仉是在q行时进行装载的?/SPAN>
GoF的设计模式中我们大量看到的是面向接口~程Q?/SPAN>Interface Driven Design 接口驱动Q接口驱动有很多好处Q可以提供不同灵zȝ子类实现Q增加代码稳定和健壮性等{,但是接口一定是需要实现的Q也是如下语句q早要执行:
AInterface a = new AInterfaceImp();
׃以上的代码被写入了调用者程序中Q同时象AinterfaceImpq样的接口的实现cL在编译时被装载的Q如果以后想加入新的接口实现cd必须修改调用者的代码?/SPAN>
IoC模式与以上情况不同,接口的实现类是在q行时被装蝲的,q样即以后新添加了接口实现cL也不需修改调用者的代码Q可以通过特定的方式来定位新增的实现类Q如配置文g指定Q?/SPAN>IoC英文?/SPAN> Inversion of ControlQ即反{模式Q这里有著名的好莱坞理论Q你呆着别动Q到时我会找你?/SPAN>
IoC模式可以延缓接口的实玎ͼҎ需要实玎ͼ有个比喻Q接口如同空的模型套Q在必要Ӟ需要向模型套注石膏,q样才能成ؓ一个模型实体,因此Q?/SPAN>对于q些新生的容器,它们反{的是“如何定位插件的具体实现”。因此, Martin Fowler l这U模式v了一个Ş象的名称“依赖注入?/SPAN>Q?/SPAN>Dependency InjectionQ?/SPAN>?INPUT class="" id=Image1 title="" type=image src="http://chaocai.cnblogs.com/Files/chaocai/1.jpg" value=Image1>
图表 1 采用Dependency Injection前后的依赖关pd?/SPAN>
依赖注入的Ş式主要有三种Q分别将它们叫做构造子注入Q?/SPAN>Constructor InjectionQ、设值方法注入(Setter InjectionQ和接口注入Q?/SPAN>Interface InjectionQ?/SPAN>
q三U方式在Martin Fowler的?/SPAN>Inversion of Control Containers and the Dependency Injection pattern》中都给Z详细的定义及说明Q本文就不再赘述了,下面的内容将着重介l?/SPAN>构造子注入模式的特点及实现Ҏ?/SPAN>
通常情况下设?/SPAN>Ҏ注入和接口注入较易于被开发h员接受,而构造子注入则应用较,实际上构造子注入h很多其他两者所不具有的优势Q?/SPAN>
1 构造子注入形成了一U更强的依赖契约
2 可以获得更加明的代码
3 更加明的依赖声明机制Q无d?/SPAN>XML配置文g或设?/SPAN>Ҏ
4 更加W合接口与实现分ȝlg特征Q组件接口表明能够向其它lg提供的服务,而实现则应该是所提供服务的实现应该与服务契约无关Q即不应包含用于获得依赖的设值方法等Q?/SPAN>
5 不会出现不确定的状态。在讑ր方法注入中Q由于ƈ不是所有的讑ր方法(setterQ都一定会被调用的Q所以会有不定状态?/SPAN>
从以上几Ҏ们还可以分析出构造子注入对于lg代码的入侉|远于其它两种模式(接口注入使得lg必须实现特定接口Q设值方法同栯求组件提供特定的setterҎ)Q代码更加易于维?/SPAN>?INPUT class="" id=Image2 title="" type=image src="http://chaocai.cnblogs.com/Files/chaocai/2.jpg" value=Image2>
图表 2 CZ中类的关p?/SPAN>
Client的实C赖于接口A?/SPAN>B?/SPAN>C的实玎ͼ但是Z提供pȝ更好的灵zL和可扩展性,各接口的实现以组件的方式利用java的反机制进行运行时装蝲Q注意到lg间可能会存在某种依赖关系Q例如组?/SPAN>AX依赖与接?/SPAN>B的实现类Q而这中依赖关pddq行时动态注入,lgZ告诉lg的调用者这U依赖关pM便注入,可以使用上文提到的各U模式:
1 使用接口注入模式
public interface InjectB{
public void injectB(B bImp);
}
public interface InjectC{
public void injectC(C cImp);
}
public class AImp implements A,InjectB,InjectC{
?/SPAN>
public void injectB(B bImp);
public void injectC(C cImp);
?/SPAN>
}
2 使用讑ր注入模?/SPAN>
public class AImp implements A {
?/SPAN>
public void setB(B bImp);
public void setC(C cImp);
?/SPAN>
}
3 使用构造子注入模式
public class AImp implements A {
?/SPAN>
public AImp(B bImp, C cImp){
?/SPAN>
}
?/SPAN>
}
׃上实例可以清楚的看出采用构造子注入模式的实现组件代码最为简单,且所受的入R性最?/SPAN>
?/SPAN>java?/SPAN>.NETq样h反射功能的语a中实现类型的q行时蝲入ƈ不复杂,只要通过Class.forName或生成自qClassLoader可以实现?/SPAN>
同样我们可以通过反射机制获取lg构造函数的参数Q注入相应接口的实现Q作者将此过E进行了装Q以下是代码Q?/SPAN>
public class RefectHelper {
public Object ConstructorHelper(String className,ConstructorParamDeal pd) throws Exception{
try{
//获取cM的构造函?/SPAN>
Constructor[] constructs=Class.forName(className).getConstructors(); //实现中默认用第一个构造函数类创徏实例
Class [] classes=constructs[0].getParameterTypes();
//获取要注入的参数实例
Object []obj=pd.dealParam(classes);
//创徏实例
return constructs[0].newInstance(obj);
}catch(Exception e){
throw e;
}
}
}
/**
*构造函数参数注?/SPAN>
**/
public interface ConstructorParamDeal {
/**
*Ҏ构造函C参数的类型注?/SPAN>,相应的实?/SPAN>
@param classes 构造函数的参数cd
?/SPAN>return 注入构造函数的参数实现
**/
public Object [] dealParam(Class [] classes);
}
public class ParamDeal implements ConstructorParamDeal{
/* (non-Javadoc)
* @see com.topsec.tsm.agent.helper.ConstructorParamDeal#dealParam(java.lang.Class[])
*/
public Object [] dealParam(Class[] classes) {
Object [] obj=new Object[classes.length];
for (int i=0;i<obj.length;i++){
//Z同类型注入选择不同实例
if (classes[i].equals(String.class)){
obj[i]=”Hello World?
}
}
return obj;
}
}
上面的程序中ConstructorHelper用于利用反射机制枚D入类的构造函数及构造函数的参数的类型,至于不同cd注入什么样的实例则?/SPAN>ContructorParamDeal的实现者来军_Q?/SPAN>ContructorParamDeal的实现者同样可以以lg的Ş式在q行时动态蝲入。由于组仉的依赖关pȝ制约Q所以组件实例化的顺序需要特别考虑?/SPAN>
三种依赖注入模式各有其特点和优势Q只有充分理解这些模式间的不同,才能q应用选择正确的依赖注入模式,文中介绍的构造子注入模式实现ҎQ在使用其他h反射功能的语aQ如Q?/SPAN>.NETQ时同样可以参考?/SPAN>
[参考文?/SPAN>]
1 Martin Fowler,Inversion of Control Containers and the Dependency Injection pattern,http://www.martinfowler.com/articles/injection.html,2004
2 Erich Gamma,Design Patterns,Addison Wesley,1999
3 http://www.picocontainer.org/
4 彭晨?/SPAN>,Ioc模式, http://www.jdon.com,2004