Ioc.docx
- 文档编号:10694664
- 上传时间:2023-05-27
- 格式:DOCX
- 页数:16
- 大小:24.48KB
Ioc.docx
《Ioc.docx》由会员分享,可在线阅读,更多相关《Ioc.docx(16页珍藏版)》请在冰点文库上搜索。
Ioc
在第2章中,笔者通过两个简单的实例展示了Spring的IoC功能,接下来将对Spring的IoC进行详细的讲解,因为Spring的核心就是IoC。
在本章中,首先从IoC的基本思想开始,然后通过实例的方式使读者对其概念和工作原理有一个深入的了解,最后会把第2章中的第一个实例进行改编,使其通过构造方式来实现同样的功能。
3.1 反向控制/依赖注入
近年来,在Java社区中掀起了一股轻量级容器的热潮,几乎每隔一段时间,就会有新的轻量级容器出现,这些轻量级的容器能够较好地帮助开发者快速地将不同的组件组装成一个应用程序。
在这些轻量级的容器的背后,有一个共同的模式决定着容器装配组件的方式,就是“反向控制”,即IoC,英文全称是InversionofControl。
MartinFowler深入地探索了“反向控制”的工作原理,并为其起了一个新的名字叫做“依赖注入”,即DI,英文全称是DependencyInjection。
关于MartinFowler的这篇文章,读者可以在其网站上看到,网址是
3.1.1 反向控制(IoC)
单从字面上,其实很难理解“反向控制”所要表达的含义。
其实在编程时,开发人员常说的“实现必须依赖抽象,而不是抽象依赖实现”就是“反向控制”的一种表现方式。
下面,笔者主要通过举例来说明这个抽象的概念。
这个实例主要说明的是如何通过IoC来实现业务逻辑从哪种数据库中取数据的问题。
可能的取数据方式有3种,分别是:
● 从SQLServer数据库中取数据。
● 从DB2数据库中取数据。
● 从Oracle数据库中取数据。
介绍这个实例的思路是:
首先介绍编写这类程序通常的做法,然后指出这种做法的不足,接着给出一种比较好的做法,即通过IoC来实现这类功能,最后对这种做法进行总结,使读者一步一步地了解IoC。
编写这类程序通常做法的具体步骤如下:
(1)通常编写这类程序都是先编写一个从数据库取数据的类SqlServerDataBase.java,这里以从SQLServer数据库中取数据为例。
SqlServerDataBase.java的示例代码如下。
其中getDataFromSqlServer()是SqlServerDataBase类中的一个方法,具体负责从SQLServer数据库中取数据。
//*******SqlServerDataBase.java**************
publicclassSqlServerDataBase{
……
//从SQLServer数据库中获取数据
publicListgetData(){
……
}
}
(2)业务逻辑类Business.java通过SqlServerDataBase.java中的方法来从SQLServer数据库中取数据。
Business.java的示例代码如下。
其中SqlServerDataBase是用来从SQLServer数据库中取数据的类。
//*******Business.java**************
publicclassBusiness{
privateSqlServerDataBasedb=newSqlServerDataBase();
……
//从SQLServer数据库中获取数据
publicvoidgetData(){
……
Listlist=db.getDataFromSqlServer();
……
}
}
可以看出以上程序编写的不足之处:
Business类依赖于SqlServerDataBase类,如果业务改变,用户现在要求从DB2或Oracle数据库中取数据,则这个程序就不适用了,必须要修改Business类。
(3)改为从DB2数据库中取数据,DB2DataBase.java的示例代码如下。
其中getDataFromDB2()是DB2DataBase类中的一个方法,具体负责从DB2数据库中取数据。
//*******DB2DataBase.java**************
publicclassDB2DataBase{
……
//从DB2数据库中获取数据
publicListgetData(){
……
}
}
(4)必须修改业务逻辑类Business.java,改为从DB2数据库中取数据。
Business.java的示例代码如下,其中DB2DataBase是用来从DB2数据库中取数据的类。
//*******Business.java**************
publicclassBusiness{
privateDB2DataBasedb=newDB2DataBase();
……
//从DB2数据库中获取数据
publicvoidgetData(){
……
Listlist=db.getDataFromDB2();
……
}
}
(5)同样,用户现在要求从Oracle数据库中取数据,则这个程序就不适用了。
改为从Oracle数据库中取数据,OracleDataBase.java的示例代码如下。
其中getDataFromOracle()是OracleDataBase类中的一个方法,具体负责从Oracle数据库中取数据。
//*******OracleDataBase.java**************
publicclassOracleDataBase{
……
//从Oracle数据库中获取数据
publicListgetData(){
……
}
}
(6)还要修改业务逻辑类Business.java,改为从Oracle数据库中取数据。
Business.java的示例代码如下。
其中OracleDataBase是用来从Oracle数据库中取数据的类。
//*******Business.java**************
publicclassBusiness{
privateOracleDataBasedb=newOracleDataBase();
……
//从Oracle数据库中获取数据
publicvoidgetData(){
……
Listlist=db.getDataFromOracle();
……
}
}
至此,读者应该可以发现了,这可不是一个好的设计,因为每次业务需求的变动都要导致程序的大量修改,怎样才能改变这种情形的发生呢?
怎样才能实现Business类的重用呢?
IoC就可以解决这个问题,它可以通过面向抽象编程来改变这种情况。
下面就利用IoC来实现Business类的重用,编写思路是:
首先编写一个获取数据的接口,然后每个具体负责从各种数据库中获取数据的类都实现这个接口,而在业务逻辑类中,则根据接口编程,并不与具体获取数据的类打交道。
通过IoC来实现这个功能的具体步骤如下。
(1)编写用来获取数据的接口DataBase。
DataBase.java的示例代码如下:
//*******DataBase.java**************
publicinterfaceDataBase{
//该方法用来获取数据
publicvoidgetData();
}
(2)编写具体负责从SQLServer数据库中取数据的类SqlServerDataBase,该类实现了接口DataBase。
SqlServerDataBase.java的示例代码如下:
//*******SqlServerDataBase.java**************
publicclassSqlServerDataBaseimplementDataBase{
//该方法用来获取数据
publicvoidgetData(){
//以下是具体从SQLServer数据库中取数据的代码
……
}
}
(3)编写业务逻辑类Business,该类只针对接口DataBase编码,而不针对实体类。
Business.java的示例代码如下:
//*******Business.java**************
publicclassBusiness{
//针对接口DataBase定义变量
privateDataBasedb;
publicvoidsetDataBase(DataBasedb){
this.db=db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
publicvoidgetData(){
……
db.getData();
……
}
}
(4)编写测试类TestBusiness。
TestBusiness.java的示例代码如下:
//*******TestBusiness.java**************
publicclassTestBusiness{
privateBusinessbusiness=newBusiness();
……
//根据注入的数据库类,从SQLServer数据库中获取数据
publicvoidgetData(){
……
business.setDataBase(newSqlServerDataBase());
business.getData();
……
}
}
通过这种方式Business类就可以重用了,不管从哪种数据库中获取数据,Business类都不用改动,只需要实现具体的DataBase接口就可以了。
例如,用户要求改为从DB2数据库中获取数据,只要实现一个具体负责从DB2数据库中取数据的类就可以了。
(5)编写具体负责从DB2数据库中取数据的类DB2DataBase,该类实现了接口DataBase。
DB2DataBase.java的示例代码如下:
//*******DB2DataBase.java**************
publicclassDB2DataBaseimplementDataBase{
publicvoidgetData(){
//以下是具体从DB2数据库中取数据的代码
……
}
}
(6)业务逻辑类Business不用作任何改动,修改测试类TestBusiness。
TestBusiness.java的示例代码如下:
//*******TestBusiness.java**************
publicclassTestBusiness{
privateBusinessbusiness=newBusiness();
……
//根据注入的数据库类,从DB2数据库中获取数据
publicvoidgetData(){
……
business.setDataBase(newDB2DataBase());
business.getData();
……
}
}
(7)如果用户又要求改为从Oracle数据库中获取数据,只要实现一个具体负责从Oracle数据库中取数据的类就可以了。
编写具体负责从Oracle数据库中取数据的类OracleDataBase,该类实现了接口DataBase。
OracleDataBase.java的示例代码如下:
//*******OracleDataBase.java**************
publicclassOracleDataBaseimplementDataBase{
publicvoidgetData(){
//以下是具体从Oracle数据库中取数据的代码
……
}
}
(8)业务逻辑类Business不用作任何改动,修改测试类TestBusiness。
TestBusiness.java的示例代码如下:
//*******TestBusiness.java**************
publicclassTestBusiness{
privateBusinessbusiness=newBusiness();
……
//根据注入的数据库类,从Oracle数据库中获取数据
publicvoidgetData(){
……
business.setDataBase(newOracleDataBase());
business.getData();
……
}
}
从上面的例子可以看到,在第一个例子中,使用通常的做法,Business类依赖于具体获取数据的类;而在第二个例子中,通过接口来编程,即控制关系的反向转移,实现了IoC功能,并使代码获得了重用。
这也就实现了上面所说的“实现必须依赖抽象,而不是抽象依赖实现”。
3.1.2 依赖注入(DI)
MartinFowler在其文章中提出了“它们反转了哪方面的控制”的问题后,就为IoC起了一个更能说明这种模式特点的新名字,叫做“依赖注入”,即DependencyInjection,Spring就是使用DependencyInjection来实现IoC功能,接着MartinFowler介绍了DependencyInjection的3种实现方式,接下来笔者将结合上面的例子对这3种实现方式进行详细的讲解。
3.2 依赖注入的3种实现方式
在讲解依赖注入的3种实现方式之前,这里先澄清一下依赖注入的意义:
让组件依赖于抽象,当组件要与其他实际对象发生依赖关系时,通过抽象来注入依赖的实际对象。
依赖注入的3种实现方式分别是:
接口注入(interfaceinjection)、Set注入(setterinjection)和构造注入(constructorinjection)。
接下来笔者还将主要通过举例的方式,把依赖注入的3种实现方式介绍给读者。
3.2.1 接口注入(interfaceinjection)
接口注入指的就是在接口中定义要注入的信息,并通过接口完成注入。
结合前面的示例,其具体步骤如下。
(1)编写一个接口IBusiness,各种数据库的注入将通过这个接口进行。
IBusiness.java的示例代码如下:
//*******IBusiness.java**************
publicinterfaceIBusiness{
publicvoidcreateDI(DataBasedb);
}
(2)任何想要使用数据库实例的类都必须实现这个接口,业务逻辑类Business实现这个接口IBusiness。
Business.java的示例代码如下:
//*******Business.java**************
publicclassBusinessimplementIBusiness{
privateDataBasedb;
publicvoidcreateDI(DataBasedb){
this.db=db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
publicvoidgetData(){
……
db.getData();
……
}
}
(3)编写测试类TestBusiness。
TestBusiness.java的示例代码如下:
//*******TestBusiness.java**************
publicclassTestBusiness{
privateBusinessbusiness=newBusiness();
……
//根据注入的数据库类,从Oracle数据库中获取数据
publicvoidgetData(){
……
business.createDI(newOracleDataBase());
business.getData();
……
}
}
如果要完成依赖关系注入的对象,必须实现IBusiness接口。
3.2.2 Set注入(setterinjection)
Set注入指的就是在接受注入的类中定义一个Set方法,并在参数中定义需要注入的元素。
为了让类Business接受DataBase的注入,需要为它定义一个Set方法来接受DataBase的注入。
Business.java的示例代码如下:
//*******Business.java**************
publicclassBusiness{
privateDataBasedb;
publicvoidsetDataBase(DataBasedb){
this.db=db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
publicvoidgetData(){
……
db.getData();
……
}
}
更详细的代码,可以参看3.1节的第二个例子,采用的就是Set注入的方式。
3.2.3 构造注入(constructorinjection)
构造注入指的就是在接受注入的类中定义一个构造方法,并在参数中定义需要注入的元素。
为了让类Business接受DataBase的注入,需要为它定义一个构造方法,来接受DataBase的注入。
Business.java的示例代码如下:
//*******Business.java**************
publicclassBusiness{
privateDataBasedb;
publicBusiness(DataBasedb){
this.db=db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
publicvoidgetData(){
……
db.getData();
……
}
}
3.3 将HelloWorld实例改为构造注入方式实现
Spring支持Set注入(setterinjection)和构造注入(constructorinjection),但更推荐使用Set注入。
上面讲过,第2章的第一个实现HelloWorld的实例就是采用Set注入方式实现的,读者可以参看第2章的实例。
下面笔者把这个实例改为采用构造注入方式实现。
改写思路是:
首先修改类HelloWorld,在该类中增加一个构造方法,然后修改Spring的配置文档config.xml,最后编写测试程序TestHelloWorld.java。
3.3.1 修改HelloWorld.java
修改com.gc.action包下的HelloWorld.java,增加一个构造方法,并把要注入的字符串msg作为参数,代码如下,在HelloWorld类中增加了一个构造方法publicHelloWorld(Stringmsg)。
//*******HelloWorld.java**************
packagecom.gc.action;
publicclassHelloWorld{
//该变量用来存储字符串
publicStringmsg=null;
//增加了一个构造方法
publicHelloWorld(Stringmsg){
this.msg
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Ioc