您的位置:首页 > 编程语言 > Java开发

spring的ioc容器的实现原理(附测试代码)

2014-03-01 13:10 645 查看
 spring如今在java开源框架中大行其道,很受欢迎,是轻量级JAVA EE中的核心框架,企业级应用信息系统开发的首选框架,它不愧是是JAVA中重量级框架EJB强大挑战对手。因为其灵活的扩展性和伸缩性,灵活简单的配置(采用xml文件和Annotation注解等),可以和众多的开源框架进行有效的整合资源,让它既可以和struts,jsf这些表现层框架,也能和hibernate,ibatis这些中间件框架可以无缝链接,还有dwr这些ajax框架!总之,spring真是个神奇而又伟大的东西!而其中,它的IOC和AOP则是spring中的核心应用.阿堂一直对spring中的IOC功能的实现,有些好奇,想探个究竟,看看这些世界顶级高手是如何实现的.阿堂在幸读到了疯狂java书籍李刚先生的《轻量级JAVA EE企业应用实战》一书中,讲架构模式时,给出sping 的ioc容器相关原理的简单代码实现后,让阿堂看了后,豁然开朗,原来spring的底层的实现原理是这样的,觉得很有收获,如下,就有如下这遍文章的出现了。看高手写的东西,会让你以后开发时,会从另一个新的高度上来看问题!多看看开源世界的成熟框架的代码,是非常重要的,这也许是高手学习必须经历的过程吧!
  下面,我就来和朋友一起来分享一下了!(在本篇文章,阿堂会附上详细的测试代码)
  通常情况下,我们使用spring的时候,会将spring配置成Listener写在web.xml中,让web容器初始化的时候,也同时加载了spring了容器,sping容器在加载的时候,实际上,它也是要进行初始化,数据源的初始化,完成bean类的实例化,bean类的依赖注入的属性的初始化等工作,下面我要分享的代码,正是基于这样的思路来进行的
   在附上如下测试代码之前,我先对李刚先生的spring的ioc代码的实现原理的代码功能,作一下简单的说明,要不然,估计会有一些朋友可能不一定能看懂这些代码的执行,因为里面有很多java反射方面的知识!
主要的几点,阿堂说明如下
(1)这些代码中,关键就是YeekuXmlApplicationContext类,它里面有两个重要的初始化方法,一个是initPool(),它主要是初始化容器中所有singleton Bean,另一个是initProp(),它主要是初始化容器中singleton Bean的属性
(2)如下代码中的ioc容器,也同样采用bean.xml配置文件的方式来管理bean,其中,主要讲了两种常见的bean类,其作用域是singleton和prototype类型
(3)在加载的时候,首先会用一个Map来封装bean对应的id号和Class类的实例(对象)
(4)其中,有如下一段代码,需要认真体会,刚开始,阿堂还真没看懂,后来,我查了jdk的api才真正弄懂(api说明我附在后面)
Method setter = null;
//遍历target对象所所实现的所有接口
for (Class superInterface : target.getClass().getInterfaces())
{
try
{
//获取设值注入所需的setter方法
setter = bean.getClass().getMethod(
"set" + propNameCamelize , superInterface);
//如果成功取得该接口对应的方法,直接跳出循环
break;
}
catch (NoSuchMethodException ex)
{
//如果没有找到对应的setter方法,继续下次循环
continue;
}
}


实际上,对于此例中,上面的break语句,可以注释掉,因为BetterPrinter implements Output本来就只实现了一个接口,如果是多个接口,此处的break是不能省掉的

在jdk的api中,有如下说明(我就不解释了)
public Class[] getInterfaces()确定此对象所表示的类或接口实现的接口。
如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。数组中接口对象的顺序与此对象所表示的类的声明的 implements 子句中的接口名顺序一致。例如,给定声明:

class Shimmer implements FloorWax, DessertTopping { ... }
设 s 的值为 Shimmer 的一个实例;表达式:
s.getClass().getInterfaces()[0]
的值为表示FloorWax 接口的 Class 对象;
s.getClass().getInterfaces()[1]
的值为表示 DessertTopping 接口的 Class 对象。
如果此对象表示一个接口,则该数组包含表示该接口扩展的所有接口的对象。数组中接口对象的顺序与此对象所表示的接口的声明的 extends 子句中的接口名顺序一致。

如果此对象表示一个不实现任何接口的类或接口,则此方法返回一个长度为 0 的数组。

如果此对象表示一个基本类型或 void,则此方法返回一个长度为 0 的数组。

返回:
该类所实现的接口的一个数组。

测试效果图





下面附上完整代码

测试类IoCTest

public class IoCTest
{
public static void main(String[] args)
throws Exception
{
//创建IoC容器
ApplicationContext ctx =
new YeekuXmlApplicationContext("bean.xml");
//从IoC容器中取出computer Bean
Computer c = (Computer)ctx.getBean("computer");
//测试Computer对象
c.keyIn("疯狂Java讲义");
c.keyIn("Struts2权威指南");
c.print();
System.out.println(ctx.getBean("now"));
}
}

关键的实现类YeekuXmlApplicationContext

public class YeekuXmlApplicationContext
implements ApplicationContext
{
//保存容器中所有单例模式的Bean实例
private Map<String , Object> objPool
= Collections.synchronizedMap(new HashMap<String , Object>());
//保存配置文件对应的Document对象
private Document doc;
//保存配置文件里的根元素
private Element root;
public YeekuXmlApplicationContext(String filePath)
throws Exception
{
SAXReader reader = new SAXReader();
String path=System.getProperties().getProperty("user.dir")+"\\src\\";
doc = reader.read(path+filePath);
root = doc.getRootElement();
initPool();
initProp();
}

public Object getBean(String name)
throws Exception
{
Object target = objPool.get(name);
//对于singleton Bean,容器已经初始化了所有Bean实例
if (target.getClass() != String.class)
{
return target;
}
else //这种情况对于非singlton Bean
{
String clazz = (String)target;
//对于prototype并未注入属性值
return Class.forName(clazz).newInstance();
}
}
//初始化容器中所有singleton Bean
private void initPool()
throws Exception
{
//遍历配置文件里的每个<bean.../>元素
for (Object obj : root.elements())
{
Element beanEle = (Element)obj;
//取得<bean.../>元素的id属性
String beanId = beanEle.attributeValue("id");
//取得<bean.../>元素的class属性
String beanClazz = beanEle.attributeValue("class");
//取得<bean.../>元素的scope属性
String beanScope = beanEle.attributeValue("scope");
//如果<bean.../>元素的scope属性不存在,或为singleton
if (beanScope == null ||
beanScope.equals("singleton"))
{
//以默认构造器创建Bean实例,并将其放入objPool中
objPool.put(beanId , Class.forName(beanClazz).newInstance());
}
else
{
//对于非singlton Bean,存放该Bean实现类的类名。
objPool.put(beanId , beanClazz);
}
}
}
//初始化容器中singleton Bean的属性
private void initProp()
throws Exception
{
//遍历配置文件里的每个<bean.../>元素
for (Object obj : root.elements())
{
Element beanEle = (Element)obj;
//取得<bean.../>元素的id属性
String beanId = beanEle.attributeValue("id");
//取得<bean.../>元素的scope属性
String beanScope = beanEle.attributeValue("scope");
//如果<bean.../>元素的scope属性不存在,或为singleton
if (beanScope == null ||
beanScope.equals("singleton"))
{
//取出objPool的指定的Bean实例
Object bean = objPool.get(beanId);

//遍历<bean.../>元素的每个<property.../>子元素
for (Object prop : beanEle.elements())
{
Element propEle = (Element)prop;
//取得<property.../>元素的name属性
String propName = propEle.attributeValue("name");
//取得<property.../>元素的value属性
String propValue = propEle.attributeValue("value");
//取得<property.../>元素的ref属性
String propRef = propEle.attributeValue("ref");
//将属性名的首字母大写
String propNameCamelize = propName.substring(0 , 1).toUpperCase()
+ propName.substring(1 , propName.length());

//如果<property.../>元素的value属性值存在
if (propValue != null && propValue.length() > 0)
{
//获取设值注入所需的setter方法
Method setter = bean.getClass().getMethod(
"set" + propNameCamelize , String.class);
//执行setter注入
setter.invoke(bean , propValue);
}
if (propRef != null && propRef.length() > 0)
{
//取得需要被依赖注入的Bean实例
Object target = objPool.get(propRef);
//objPool池中不存在指定Bean实例
if (target == null)
{
//此处还应处理Singleton Bean依赖prototype Bean的情形
}
//定义设值注入所需的setter方法
Method setter = null;
//遍历target对象所所实现的所有接口
for (Class superInterface : target.getClass().getInterfaces())
{
try
{
//获取设值注入所需的setter方法
setter = bean.getClass().getMethod(
"set" + propNameCamelize , superInterface);
//如果成功取得该接口对应的方法,直接跳出循环
break;
}
catch (NoSuchMethodException ex)
{
//如果没有找到对应的setter方法,继续下次循环
continue;
}
}
//如果setter方法依然为null,
//则直接取得target实现类对应的setter方法
if (setter == null)
{
setter = bean.getClass().getMethod(
"set" + propNameCamelize , target.getClass());
}
//执行setter注入
setter.invoke(bean , target);
}
}
}
}
}
}

spring的ioc容器的配置文件bean.xml

<?xml version="1.0" encoding="GBK"?>
<beans>
<bean id="computer" class="lee.Computer">
<!-- 为name属性注入普通属性值 -->
<property name="name" value="网络时空的电脑"/>
<!-- 为out属性注入普通属性值 -->
<property name="out" ref="betterPrinter"/>
</bean>
<!-- 配置两个Bean实例 -->
<bean id="printer" class="lee.Printer"/>
<bean id="betterPrinter" class="lee.BetterPrinter"/>
<!-- 配置一个prototype行为的Bean实例 -->
<bean id="now" class="java.util.Date" scope="prototype"/>
</beans>

接口类Output

public interface Output
{
//接口里定义的属性只能是常量
int MAX_CACHE_LINE = 50;
//接口里定义的只能是public的抽象实例方法
void out();
void getData(String msg);
}

接口类

public interface ApplicationContext
{
//获取指定Bean实例的方法
Object getBean(String name)
throws Exception;
}

实现类BetterPrinter

public class BetterPrinter implements Output
{
private String[] printData = new String[MAX_CACHE_LINE * 2];
//用以记录当前需打印的作业数
private int dataNum = 0;
public void out()
{
//只要还有作业,继续打印
while(dataNum > 0)
{
System.out.println("高速打印机正在打印:" + printData[0]);
//把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData , 1, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if (dataNum >= MAX_CACHE_LINE * 2)
{
System.out.println("输出队列已满,添加失败");
}
else
{
//把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++] = msg;
}
}
}

实现类Printer

public class Printer implements Output
{
private String[] printData = new String[MAX_CACHE_LINE];
//用以记录当前需打印的作业数
private int dataNum = 0;
public void out()
{
//只要还有作业,继续打印
while(dataNum > 0)
{
System.out.println("打印机打印:" + printData[0]);
//把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData , 1, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if (dataNum >= MAX_CACHE_LINE)
{
System.out.println("输出队列已满,添加失败");
}
else
{
//把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++] = msg;
}
}
}

普通类Computer

public class Computer
{
private Output out;
private String name;

public Computer(){}

//out属性的setter和getter方法
public void setOut(Output out)
{
this.out = out;
}
//name属性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
//定义一个模拟获取字符串输入的方法
public void keyIn(String msg)
{
out.getData(msg);
}
//定义一个模拟打印的方法
public void print()
{
System.out.println(name + "开始打印...");
out.out();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: