摘要:本文对IoC模式、依赖注入(Dependency Injection) 模式做了简要介绍,文中分析构造子注入模式与其他模式相比较的优势和特点,并给出了在JAVA中实现该模式的方法。
1 引言
IoC(Inversion of Control)模式以被目前的轻量级容器所广泛应用,通过IoC模式这些容器帮助开发者将来自不同项目的组件装配成一个内聚的应用程序。轻量级的IoC容器(如Spring、pico-container)虽然为我们的开发提供了很大的便利,但是在很多情况下这些轻量级容器所提供的功能并不一定非常适合我们的需要,也许这些容器的功能过于庞大了,或者所提供的功能缺乏对特定应用的针对性,或者我们需要更高的运行效率,这时我们可以在了解IoC的原理的基础上利用JAVA的反射机制自己实现灵活的、可扩展的组件机制。
2 IoC与依赖注入(Dependency Injection)模式简介
与GoF的设计模式相同,IoC模式同样是关注重用性,但与GoF模式不同的是IoC模式更加关注二进制级的重用性和可扩展性,即可以直接通过二进制级进行扩充,复用的模块通常被称为组件或者插件,组件和插件都是在运行时进行装载的。
GoF的设计模式中我们大量看到的是面向接口编程:Interface Driven Design 接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:
AInterface a = new AInterfaceImp();
由于以上的代码被写入了调用者程序中,同时象AinterfaceImp这样的接口的实现类是在编译时被装载的,如果以后想加入新的接口实现类则必须修改调用者的代码。
IoC模式与以上情况不同,接口的实现类是在运行时被装载的,这样即使以后新添加了接口实现类是也不需修改调用者的代码(可以通过特定的方式来定位新增的实现类,如配置文件指定)。IoC英文为 Inversion of Control,即反转模式,这里有著名的好莱坞理论:你呆着别动,到时我会找你。
IoC模式可以延缓接口的实现,根据需要实现,有个比喻:接口如同空的模型套,在必要时,需要向模型套注射石膏,这样才能成为一个模型实体,因此,对于这些新生的容器,它们反转的是“如何定位插件的具体实现”。因此, Martin Fowler 给这种模式起了一个形象的名称“依赖注入”(Dependency Injection)。
图表 1 采用Dependency Injection前后的依赖关系变化
依赖注入的形式主要有三种,分别将它们叫做构造子注入(Constructor Injection)、设值方法注入(Setter Injection)和接口注入(Interface Injection)。
这三种方式在Martin Fowler的《Inversion of Control Containers and the Dependency Injection pattern》中都给出了详细的定义及说明,本文就不再赘述了,下面的内容将着重介绍构造子注入模式的特点及实现方法。
3 构造子注入模式的特点及实现
3.1 构造子注入模式的特点
通常情况下设值方法注入和接口注入较易于被开发人员接受,而构造子注入则应用较少,实际上构造子注入具有很多其他两者所不具有的优势:
1 构造子注入形成了一种更强的依赖契约
2 可以获得更加简明的代码
3 更加简明的依赖声明机制,无须定义XML配置文件或设值方法
4 更加符合接口与实现分离的组件特征,组件接口表明能够向其它组件提供的服务,而实现则应该是所提供服务的实现应该与服务契约无关(即不应包含用于获得依赖的设值方法等)。
5 不会出现不确定的状态。在设值方法注入中,由于并不是所有的设值方法(setter)都一定会被调用的,所以会有不确定状态。
从以上几点我们还可以分析出构造子注入对于组件代码的入侵性远小于其它两种模式(接口注入使得组件必须实现特定接口,设值方法同样要求组件提供特定的setter方法),代码更加易于维护。
图表 2 示例中类的关系
Client的实现依赖于接口A、B和C的实现,但是为了提供系统更好的灵活性和可扩展性,各接口的实现以组件的方式利用java的反射机制进行运行时装载,注意到组件间可能会存在某种依赖关系,例如组件AX依赖与接口B的实现类,而这中依赖关系必须在运行时动态注入,组件为了告诉组件的调用者这种依赖关系以便注入,可以使用上文提到的各种模式:
1 使用接口注入模式
public interface InjectB{
public void injectB(B bImp);
}
public interface InjectC{
public void injectC(C cImp);
}
public class AImp implements A,InjectB,InjectC{
…
public void injectB(B bImp);
public void injectC(C cImp);
…
}
2 使用设值注入模式
public class AImp implements A {
…
public void setB(B bImp);
public void setC(C cImp);
…
}
3 使用构造子注入模式
public class AImp implements A {
…
public AImp(B bImp, C cImp){
…
}
…
}
由以上实例可以清楚的看出采用构造子注入模式的实现组件代码最为简单,且所受的入侵性最小。
3.2 在JAVA中实现构造子注入模式
在java及.NET这样具有反射功能的语言中实现类型的运行时载入并不复杂,只要通过Class.forName或生成自己的ClassLoader就可以实现。
同样我们可以通过反射机制获取组件构造函数的参数,注入相应接口的实现,作者将此过程进行了封装,以下是代码:
public class RefectHelper {
public Object ConstructorHelper(String className,ConstructorParamDeal pd) throws Exception{
try{
//获取类中的构造函数
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;
}
}
}
/**
*构造函数参数注入
**/
public interface ConstructorParamDeal {
/**
*根据构造函数中参数的类型注入,相应的实现
@param classes 构造函数的参数类型
◎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++){
//为不同类型注入选择不同实例
if (classes[i].equals(String.class)){
obj[i]=”Hello World”;
}
}
return obj;
}
}
上面的程序中ConstructorHelper用于利用反射机制枚举出载入类的构造函数及构造函数的参数的类型,至于不同类型注入什么样的实例则由ContructorParamDeal的实现者来决定,ContructorParamDeal的实现者同样可以以组件的形式在运行时动态载入。由于组件间的依赖关系的制约,所以组件实例化的顺序需要特别考虑。
4 结束语
三种依赖注入模式各有其特点和优势,只有充分理解这些模式间的不同,才能为自己的应用选择正确的依赖注入模式,文中介绍的构造子注入模式实现方法,在使用其他具有反射功能的语言(如:.NET)时同样可以参考。
分享到:
相关推荐
依赖注入(Dependency Injection,简称DI)是一种软件设计模式,它主要解决了对象之间的耦合问题,提高了代码的可测试性、可维护性和可扩展性。在依赖注入中,一个对象(称为依赖者)不直接创建或查找它所依赖的对象...
Dependence Walker,通常简称为Depends,是一款强大的应用程序依赖关系分析工具,主要用来解析和可视化Windows平台上EXE和DLL文件的依赖关系。它对于开发者、系统管理员和IT专业人士来说是必不可少的工具,尤其是在...
在压缩包中的文件名称列表中,我们可以看到一些关于这些主题的资源,如"Spring—Ioc(即Dependence Injection)依赖注入的3种方式"、"详解 Spring3_0 基于Annotation 的依赖注入实现"、"Spring 3_0 注解注入详解"以及...
在软件开发中,依赖注入(Dependency Injection,简称DI)是一种设计模式,它可以帮助我们构建松耦合、可测试且易于维护的代码。Kotlin-inject是Kotlin社区为Kotlin语言提供的一种轻量级的依赖注入解决方案,它旨在...
本文通过引入Copula函数来解决水文频率分析中变量依赖性的问题,提出了一种新的依赖性模拟方法。Copula是一种用于描述多个变量间依赖结构的函数,其核心优势在于灵活性和易于估计。在过去二十年中,Copula在决策制定...
Dependency Walker,通常缩写为Depends,是一款强大的分析工具,主要用在Windows操作系统环境中,用于检查和理解C++编译的可执行文件或动态链接库(DLL)所依赖的其他DLL模块。这款软件能帮助开发者识别和解决程序...
**依赖逻辑**(Dependence Logic)是一种扩展了传统一阶逻辑的新逻辑体系,它在形式逻辑领域内引入了一个重要的新概念——依赖(dependence)。依赖逻辑的核心在于研究变量之间的依赖关系,并以此为基础构建了一套完整的...
《Windows 10下的高效DLL依赖关系分析工具——Dependencies-for-win10》 在软件开发过程中,尤其是在Windows 10操作系统环境下,理解程序所依赖的动态链接库(DLL)是至关重要的。DLL文件是Windows系统中的一种共享...
在开发或调试过程中,有时我们需要了解某个DLL文件依赖于哪些其他DLL,以解决加载错误或兼容性问题。"Dependency Walker"正是这样一款强大的工具,能够帮助我们分析DLL的依赖关系。 Dependency Walker,又名...
Spring 框架的核心基础 API 是基于 IOC/DI 思想,IOC 代表 Inverse Of Control 或者 控制反转,DI 代表 Dependence Injection 或者 依赖注入。IOC/DI 思想的主要作用是简化代码、降低对象之间的耦合度,提供系统...
文章标题为“Dependence of Eigenvalues on the Regular Fourth-Order Sturm-Liouville Problem”,其主要内容是关于四阶正则Sturm-Liouville问题特征值的依赖性研究。Sturm-Liouville问题是一类重要的边界值问题,...
Eclipse DI and java annomation
Dependence Injection:spring ORM:mybatis 数据库:mysql 首页地址 http://localhost:8080/index.do 账号:可自行注册 密码:可自行注册 后台管理地址 http://localhost:8080/backLoginPage.do 账号...
Averaged One-Dependence Estimators (AODE) is one of supervised learning algorithm, which relaxes the conditional independent assumption that working on the standard naive Bayes learner. The AODE has ...
包含了Eclipse安装Activiti Designer需要的三个Jar包 org.eclipse.emf.validation_1.7.0.201306111341.jar, org.eclipse.emf.transaction_1.4.0.v20100331-1738.jar, org.eclipse.emf.workspace_1.5.1.v20120328-...
这个“LLVM_exp1_dependence_list.7z”文件可能包含的是一个与LLVM项目相关的实验或分析结果,特别是关于依赖性的列表。虽然没有提供具体的标签来进一步描述文件内容,我们可以基于LLVM的基本概念和功能来探讨一些...
Temperature dependence of electron-spin coherence in intrinsic bulk GaAs,赖天树,Xiaodong Liu,Temperature dependence of electron-spin coherence dynamics is investigated for an intrinsic bulk GaAs in...
**grunt-module-dep** 是一个基于 **Grunt** 的插件,主要用于自动化处理JavaScript模块的依赖注入。在JavaScript开发中,特别是在大型项目中,管理模块间的依赖关系是至关重要的。这个插件帮助开发者将模块的依赖...
依赖倒置原则(Dependence Inversion Principle,简称DIP)这个名字看着有点别扭,“依赖”还“倒置”,这到底是什么意思?依赖倒置原则的原始定义是:High level modules should not depend upon low level modules...
在Java中,设计模式的应用不仅限于这些基础模式,还包括对框架的理解和使用,如Spring框架中的依赖注入(Dependency Injection,DI)和控制反转(Inversion of Control,IoC)就是对工厂模式和策略模式的扩展。...