Spring之IOC思想的理解和简单实现
所谓IOC,其全称为“Inversion Of Control”,较常见的中文翻译是“控制反转”,或许有些朋友会觉得看英文名称更容易理解,其实都无所谓,重要的是我们要理解其思想。通俗一点来讲就是:将bean对象的控制权(包括对象的创建和维护)由应用代码转交到外部容器去。这里,也许会有朋友不理解什么是bean对象,其实,对于mvc三层结构的web应用程序中,模型层中所包含的对象都可以理解为是bean对象。形象点来说,一个个模型对象可以想象为一个个bean(豆子),而管理这些bean对象的容器(譬如Spring容器)则可以理解为现实生活中的一个器皿。像dao对象、vo对象无疑都是可以称为bean对象的。好了,现在言归正传,我们继续来讨论IOC思想,“将bean对象的控制权由应用代码转交到外部容器”这句话听起来或许不是那么的通俗易懂。简单一点来说,对于一个面向对象的应用程序,它必定是由若干个类组成,类其实是个静态的概念,而当程序运行起来的时候,我们就应该说程序是由若干个对象组成的,或者更准确地说,程序的运作是由若干个对象之间的相互作用而实现的。那么,对象之间是如何相互作用的呢?实际上,对象间的相互作用无非就是通过对象间的相互调用来进行的。而一个对象要想调用(或者称之为访问)另一个对象,那么调用者对象就必须能够获得被调用者对象的一个引用,这里的获得其实可以通过对象间的关联关系或者依赖关系来完成(简单的说,关联关系就是一个对象作为另一个对象的属性存在,而依赖关系就是一个对象作为另一个对象的方法参数或者返回值存在)。我想大家都知道的是,一个引用对象必须要指向一个实例化对象,才是有意义的,就好像一个地址必须指向一个实实在在的地点才是有意义的。那么,如何才能让一个引用指向一个实例化对象呢?实际上,从某个角度来分的话,将一个引用指向一个实例化对象有两种实现方式,一种是在应用代码中进行,也就是说将对象的实例化工作放在应用代码中进行;另一种则是将对象的实例化工作交给底层框架(例如Spring框架)来做,然后当程序运行时再将实例化了的对象动态地绑定到其引用对象上去。对于第二种实现方式,实际上就是我们的IOC思想的体现,也就是将bean对象的控制权由应用代码转交到外部容器中去。
接着,我们来分析一下IOC思想的具体实现。实际上,IOC思想遵循着java程序设计中的依赖倒转原则,也就是说我们在编程时应尽量用抽象和接口来编程,而不要用具体的类来编程(或者说程序的实现要依赖于抽象的接口而不依赖于具体的实现类)。那么,IOC思想具体是怎么实现的呢?这里,需要引入的一个概念就是,工厂方法模式。我想,有过编程经验的朋友,对于这个模式应该是不会陌生的,例如JDBC中获取connection对象的方式实际上就用了这个模式,大家可以回忆一下,是不是直接通过一个getConnection方法(在工厂方法模式中我们称之为工厂方法)就可以获得一个connection对象呢?是的,我们要做的就只是往该方法中传入三个String类型的参数(包括String url, String user, String password)。那么,传统的工厂方法模式是如何实现的呢?实际上该模式的实现需要用到java中的反射机制,在该模式中,一个具体的工厂对应着一种具体的产品(或者说一个具体的工厂生产一种具体的产品),所有的具体工厂都必须实现一个抽象的工厂接口,所有的具体产品都必须实现同一个抽象的产品接口。这样,我们在代码中就可以直接使用抽象的工厂和产品来编程了(也就是面向抽象编程),而不需要用到具体的工厂和产品,然后,我们可以把具体的工厂类的路径信息写到xml配置文件中去。在应用代码中,我们可以借用dom解析技术来获取配置文件中配置好的具体的工厂类的路径信息,然后通过反射机制,来载入具体的工厂类并实例化对象(这里,我们称之为注入),当我们需要改变客户端的具体产品对象时,我们不需要更改客户端的任何一行代码,而只需要改变配置文件中的具体工厂类的路径信息就可以了。对于工厂方法模式,这里我不再做详细解释,感兴趣的朋友可以自己去查看相关文档,下面是工厂方法模式的类图:
上面提到了java中反射机制的概念,那么,究竟什么是反射呢?所谓反射机制,简单点来说,就是如果我们想要在程序中获得一个类的对象,不需要直接去实例化,只需要让类的加载器帮我们去加载我们需要的类的模板对象(所谓类的模板对象,也就是Class类型的对象),然后通过调用类模板对象自身的实例化方法就可以得到我们想要的实例化对象了。这个过程中,我们只需要做的一件事就是向类的加载器传入我们想要加载的类的路径信息。
通过上面的描述,我想大家对工厂方法模式和反射都或多或少有些了解了。接着,我们就继续来分析IOC思想的实现,还记得上面我们谈到的“将bean对象的控制权由应用代码转交给外部容器”这个IOC思想的解释吧?其实,这个外部的容器(这里称之为IOC容器)就可以理解为是一个大工厂,而IOC思想则可理解为是工厂方法模式的升华。因为工厂方法模式中,具体产品的实例化操作是写死在具体工厂的工厂方法中的,而IOC容器中则是利用反射机制将具体工厂和具体产品解耦(也就是具体工厂的实现不再直接依赖具体产品,因为具体的产品被定义在配置文件中了。)。另外,工厂方法模式中,一个具体的产品必须对应一个具体的工厂,而IOC实现则是只用一个大的工厂来负责生产所有的产品,这大大降低了类的数量。此外,由于IOC容器要负责那些bean对象的管理工作,所以,IOC容器中必须要有一个集合来保存这些已经实例化了的且生命期还没结束的bean对象。这里我们用的是Map<String, Object>数据结构,这样我们就可以通过bean对象的名称来从IOC容器获取相应的bean对象了。我想,大家看了上面的文字描述可能仍会感觉很迷糊,呵呵,或许是本人的文笔水品有限吧。下面我们来看一下,对于IOC思想的一个简单实现的例子:
1. 编写IOC容器要用到的配置文件(用来配置bean对象)——beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="userDao" class="mySpringTest.UserDao">
</bean>
</beans>
2. 编写bean配置类,用来获取配置文件中所配置的bean对象的信息(即将配置文件中的配置信息封装为一个对象,这实际上用了面向对象的思想)——BeanIndicator.java
package mySpringTest;
/**
* bean对象的配置信息类
* @author Administrator
*
*/
public class BeanIndicator
{
//定义bean类的ID和名称
private String id;
private String className;
public BeanIndicator(String id, String className)
{
this.id=id;
this.className=className;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
3. 编写自定义IOC容器,负责管理配置文件中所配置的bean对象——SpringContainer.java
package mySpringTest;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
/**
* 自定义的IOC容器
* 剖析spring管理bean的原理
* 使用dom4j解析XML时,要快速获取某个节点的数据,使用XPath是个不错的方法,
* 要使用这个方法则需要导入两个包:dom4j-1.6.1.jar-306 KB、jaxen-1.1-beta-6.jar-238 KB
* @author Administrator
*
*/
public class SpringContainer
{
//定义bean队列、bean的map集合(不能有重复的对象)
private java.util.List<BeanIndicator> beanIndicators=new java.util.ArrayList<BeanIndicator>();
private java.util.Map<String,Object> singletons=new java.util.HashMap<String,Object>();
public SpringContainer(String fileName)
{
this.readXML(fileName);
this.instanceBean();
}
/**
* 解析配置好的xml文件,读取bean指示对象,并添加到队列中
* @param fileName
*/
private void readXML(String fileName)
{
//创建一个文件xml文件读取器
SAXReader saxReader=new SAXReader();
Document document=null;
try
{
//取得类的类装载器,通过类装载器,取得类路径下的文件
java.net.URL xmlPath=this.getClass().getClassLoader().getResource(fileName);
//读取文件内容
System.out.println("xmlPath="+xmlPath);
document=saxReader.read(xmlPath);
//创建一个map对象
java.util.Map<String, String> nameSpaceMap=new java.util.HashMap<String,String>();
//加入命名空间
nameSpaceMap.put("nameSpace", "http://www.springframework.org/schema/beans");
//创建beans/bean查询路径
XPath xpath=document.createXPath("nameSpace:beans/nameSpace:bean");
//设置命名空间
xpath.setNamespaceURIs(nameSpaceMap);
//获取文档下所有bean节点
java.util.List<Element> beans=xpath.selectNodes(document);
System.out.println("bean对象的个数为:"+beans.size());
for(int i=0; i<beans.size(); i++)
{
Element element=beans.get(i);
//获取bean对象的id属性
String id=element.attributeValue("id");
System.out.println("bean对象的id为:"+id);
//获取bean对象的className属性
String className=element.attributeValue("class");
//创建bean指示对象
BeanIndicator bi=new BeanIndicator(id,className);
beanIndicators.add(bi);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 实例化bean对象
*
*/
private void instanceBean()
{
for(int i=0; i<beanIndicators.size(); i++)
{
BeanIndicator bi=beanIndicators.get(i);
try
{
if(bi.getClassName()!=null&&!"".equals(bi.getClassName().trim()))
{
//通过反射机制实例化xml配置文件中配置的bean对象
Object obj=Class.forName(bi.getClassName()).newInstance();
//将实例化的bean对象放到spring容器所保存的bean集合中进行管理
singletons.put(bi.getId(), obj);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
public Object getBean(String beanName)
{
return singletons.get(beanName);//通过配置文件中配置的bean对象的id来取bean对象
}
}
4. 编写一个dao类,用以做测试——UserDao.java
package mySpringTest;
public class UserDao
{
//模拟保存方法
public void save()
{
System.out.println("user对象保存成功!");
}
}
5.编写测试类——IocTest.java
package mySpringTest;
public class IocTest
{
public static void main(String[] args)
{
SpringContainer sc=new SpringContainer("mySpringTest/beans.xml");
UserDao userDao=(UserDao)sc.getBean("userDao");
userDao.save();
}
}
6.测试结果如下:
xmlPath=file:/F:/myeclipse6_workspace/netjava_web_project/WebRoot/WEB-INF/classes/mySpringTest/beans.xml
bean对象的个数为:1
bean对象的id为:userDao
user对象保存成功!
- 大小: 3.1 KB
分享到:
相关推荐
Spring Ioc(Inversion of Control,控制反转)是Spring框架的核心特性之一,它改变了传统应用程序中对象的创建和管理方式。在传统的软件设计中,对象的创建和依赖关系的维护通常由代码自身来完成,而在Spring Ioc中...
在本文中,我们将深入探讨如何使用Spring的Inversion of Control(IoC)容器来实现一个具体的案例:控制打印机(Printer)类与不同类型的纸张(Paper)类的交互。Spring的IoC允许我们解耦组件,使代码更加灵活且易于...
在Java世界中,Spring框架以其强大的依赖注入(Dependency Injection,简称DI)和控制反转(Inversion of Control,简称IOC)能力,成为企业级应用开发的首选框架之一。理解并模拟Spring的IOC机制对于深入学习Spring...
这个“一个简单的模仿spring的ioc实现”项目旨在帮助开发者理解Spring Ioc的核心概念,并通过自己的代码实现来加深对这一机制的理解。 首先,Spring Ioc的主要目标是解耦应用程序组件。它通过管理对象的生命周期和...
例如,`SpringIOC`目录中的配置文件(如`applicationContext.xml`)用于定义bean的定义和它们之间的依赖关系。通过XML或注解方式声明bean,Spring可以自动管理bean的实例化、初始化和销毁,从而简化了代码并提高了可...
Spring的IoC是它的一个关键设计模式,其核心思想是将对象的创建和管理交给容器来处理,而不是由对象自身负责。这样可以提高代码的可测试性、可维护性和松耦合性。IoC的主要实现方式就是通过DI,即将一个对象依赖的...
在Spring框架中,依赖注入(Inversion of Control, IoC)和面向切面编程(Aspect Oriented Programming, AOP)是两大核心特性。本篇将深入探讨如何通过注解方式来模拟Spring的这两种机制,帮助你理解其底层原理。 #...
本文将深入剖析Spring的IOC容器,理解其工作原理和重要功能,以帮助开发者更好地利用这一强大的工具。 一、IOC容器的概念 IOC,即控制反转,是面向对象编程中的一个设计原则,它的主要思想是将对象的创建和管理权...
在Java世界里,Spring框架是应用最广泛的轻量级框架之一,它的核心特性包括IOC(Inversion of Control,反转控制)和AOP(Aspect Oriented Programming,面向方面编程)。这两个概念是Spring框架的灵魂,极大地提升...
理解这两者的关系有助于我们更好地设计和实现面向对象的应用。 在实际应用中,有时会遇到注入特殊复杂的属性,例如循环依赖、延迟加载、工厂方法创建的bean等。Spring通过Bean的生命周期管理和作用域(如Singleton...
本章主要介绍Spring框架中的控制反转(Inversion of Control,IoC)思想,以及依赖注入(Dependency Injection,DI)与依赖查找(Dependency Lookup,DL)两种实现IoC的方式。此外,还涉及了Spring程序开发的基本...
Spring IOC(Inversion of Control,控制反转)是Spring框架的核心特性,它将对象的创建和管理权交给了Spring容器,从而让开发者从繁琐的依赖管理中解脱出来,更专注于业务逻辑的实现。在这个名为"spring_IOC实现...
在Spring框架中,IOC(Inversion of Control,控制反转)是其核心概念之一,它改变了传统应用程序中的对象创建和管理方式。本学习资料主要聚焦于Spring的IOC容器,特别是从最基本的BeanFactory开始,逐步深入理解...
在本节中,我们将对 Spring IoC 加载流程进行详细的讲解,并探讨 IoC 思想和依赖倒置原则的应用。 IoC 控制反转 IoC(Inversion of Control)是指在软件设计中,将对象实例的控制权从代码控制剥离到容器控制。这样...
在 Spring 中,IOC 实现的关键是 IOC 容器,它是一个 BeanFactory,负责管理和控制对象的生命周期。创建对象的三种方式如下: - **通过无参构造方法:** IOC 容器使用类的无参构造器创建对象。 - **通过有参构造...
Spring框架是Java开发中不可或缺的一部分,它主要由两个核心组件构成:IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)。本笔记将深入探讨这两个概念以及它们在Spring中...
而在IOC模式下,开发者只需定义对象的接口和实现,Spring容器负责实例化、装配这些对象以及管理它们的生命周期。这样,对象之间的耦合度降低,代码更加模块化,便于测试和维护。 实现IOC的方式主要有两种:依赖注入...