`
明日天涯
  • 浏览: 35457 次
  • 性别: Icon_minigender_1
  • 来自: 福州
文章分类
社区版块
存档分类
最新评论

Spring之IOC思想的理解和简单实现

阅读更多
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 思想

    初步理解 Spring IOC 思想Spring IOC大体思想示例说明并辅助理解示例一(摘自狂神的视频):示例二(买果汁):抽取思想总结 Spring IOC 最近跟着B站的狂神学习了Spring的大概,以前有过写传统JavaWeb的经验,现在也...

    Spring——IOC(控制反转)与DI(依赖注入).docx

    控制反转IOC(Inversion of Control)是一种设计思想,DI(依赖注入)是实现IOC的一种方法 。在没有IOC的程序中,我们使用面向对象编程,对象的创建于对象间的依赖完全硬编码在程序中,对象的创建有程序自己控制;...

    实践彻底理解IOC思想

    IOC英文是Inversion of Control中文理解为控制反转,是Spring框架的一种设计思想。表示创建或者控制对象的权利由应用程序转换到Spring容器,如下将通过new关键字准确获取UserServiceImpl、UserMapperImpl类的权利...

    一直在学习Spring,这个是模仿SpringIOC的实现原理仿制的一个框架,希望能在不断地构建以及重构这个框架的过程中不断地学

    这个是模仿SpringIOC的实现原理仿制的一个框架,希望能在不断地构建以及重构这个框架的过程中不断地学习Spring,在这个过程中一窥Spring的精髓,以及我们要如何设计一个可用性高,高拓展的项目,它会教会我们如何去...

    用项目案例彻底理解SpringIOC容器

    ●运用工厂模式设计程序●理解JavaBean和POJO对象●理解控制反转思想●理解IOC容器在一个乡村小学校,一天只上三节课,有三名老师和一个校长。张老师负责教学生语文,王老师教学生数学,李老师教音乐,校长负责安排...

    SSH(Struts1.0+Spring+Hibernate)框架集成笔记

    SSH框架集成是较复杂和难理解的,只有在不断的练习和使用中才能慢慢的理解其中的原理,仅凭看视频是远远不够的,因为这些涉及了尤其是spring底层的好多类以及控制翻转(IOC)和面向切面(AOP)编程的思想,不过在讲述...

    Spring技术内幕

    , 国内第一本基于Spring3.0的著作,从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从木书中参透Spring框架的优秀架构和设计思想,而且还能从Spring优雅的实现源码...

    Spring基础与快速入门

    3 IOC:控制反转,谓之“依赖关系的转移”,如果以前都是依赖于实现,那么现在反转为依赖于抽象吧,其实它的核心思想就是要面向接口编程,至于何谓接口何谓抽象,以及它们的好处,多看看设计模式吧,这里就不费口舌...

    spring.net中文手册在线版

    8.2.接口和实现 第九章. Spring.NET杂记 9.1.简介 9.2.PathMatcher 9.2.1.通用规则 9.2.2.匹配文件名 9.2.3.匹配子目录 9.2.4.大小写需要考虑,斜线可以任意 第十章. 表达式求值 10.1.简介 10.2.表达式求值 10.3....

    Spring中眼花缭乱的BeanDefinition.docx

    为什么要读Spring源码,有的人为了学习Spring中的先进思想,也有的人是为了更好的理解设计模式,当然也有很大一部分小伙伴是为了应付面试,Spring Bean的生命周期啦,Spring AOP的原理啦,Spring IoC的原理啦,应付...

    spring详细图解

    详细说明spring功能及开发流程,开发思想!帮助你理解学习spring开发步骤!

    Spring深入理解

    这张图上展示了一个基于SSH,B/S结构的单体Java应用的搭建过程,首先通过浏览器进入到Filter拦截器,进入到Structs2,通过Service层进入到Hibernate,从而进行数据库,经过而这整个过程都是由SpringIOC容器控制。...

    20万字必备java面试八股文宝典-多线程.数据库.Spring.SpringBoot.Linux.分布式.设计模式.面试指导

    Spring框架的核心概念、IoC(控制反转)和DI(依赖注入)原理、AOP(面向切面编程)等内容。覆盖了Spring Boot和Spring Cloud。 设计模式部分详细介绍了常用的设计模式及其在Java开发中的应用场景。 适合各阶段的...

    java面试题

    spring的IOC和DI? 答:控制反转和依赖注入是spring的两大基础作用。主要是将所有组件在spring提供的外部容器中加载。提高安全性,减低耦合性,使各个框架或者类之间的依赖性降低。 什么是声明式的事务管理?为什么要...

    PHP进阶学习之依赖注入与Ioc容器详解

    最早在java的spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。目前许多主流PHP框架也使用了依赖注入容器,如ThinkPHP、Laravel等。 ...

    品味Spring 的魅力

    Spring的哲学是在不影响Java对象的设计的情况下将Java对象加入到框架中。 EJB的框架采用了一种侵略性(Invasive)的...因为IOC的思想要求bean之间不能够直接调用,而应该采用一种被动的方式进行协作。所以bean的管理是s

    Java高阶必备技术:Spring必知必会

    本章首先会从Spring前世今生和Spring中重要的设计思想开始讲起。逐步介绍Spring新特性,手写代码带领大家理解spring源码,并且会集成security等框架,为后续学习Spring Boot打下基础。 为什么需要学习这门课程? ...

Global site tag (gtag.js) - Google Analytics