博客创建了2年多了,一直没敢写点东西,怕技术不够误导了别人。2年多后的今天我已经很有信心能够为需要帮助的人做点微薄的贡献了。这是我第一次写博客,先自我介绍一下。本人网名泪滴,一个非常伤心的名字,生活中除了代码一无所有,平时喜欢看开源框架的源码,今天也为开源贡献一份自己的力量。

  这次项目叫做IOC框架,是根据spring的IoC的使用风格使用自己的代码实现。项目的目的不是为了推销我的框架,只是为了让目前正在使用IoC和将要使用IoC的小伙伴们对IoC有一个全新的认识,相信泪滴,我不会浪费你们宝贵的时间,不管你是新手还是大牛都会从中有所收获。由于我有朝九晚五的工作,精力有限,所以本次项目采用连载的方式,尽量保证每周至少2,3次更新,每次更新至少解决一个阶段性的问题。废话我就不多说了,下面开始正题。

  IoC框架,估计使用过java,使用过spring的人都不会陌生,它就是一个依赖注入的功能,通俗的说就是可以在项目或者服务启动使用的时候给我们一次性准备好我们程序中配置好的所需要的对象,并且自动将对象set到需要的对象中。例如A类中的成员变量中有一个B类的引用,那么生成对象的时候会生成一个A的对象和B的对象,并且将B的对象通过A的构造方法或者set方法将B对象设置到A对象中。其实看来完成的功能很简单就是为了让我们在代码中减少使用new关键字的次数,让大多数程序中的new的性能开销和代码开销集中在项目启动或者服务启动的时候。在程序的编写过程中不会一次又一次的写A a = new A()这种没有营养的代码,在程序运行的时候不会因为多次的new对象浪费时间。

  注意上面我们提到的IoC的功能我们可以发现几个很关键的地方。第一:该框架产生的动作就是生成对象,第二:生成了对象还要考虑该对象是不是需要依赖别的对象,如果需要要给他set好需要的对象,第三:前两个动作产生的时机一般就是项目启动或者服务启动的时候。这时候我们是不是大脑中已经有一个很简单的想法了,我们将项目中所需要的生成的对象全部配置在配置文件中,然后在main方法中或者是web中的监听器Listener调用一个方法去读取配置文件然后将他们的对象全部生成出来。至于配置文件采用什么格式的配置文件也是需要考虑的地方,由于有些对象需要依赖其他对象,而且依赖的对象个数也不一定,也有可能A依赖B,B又依赖C...这种层级关系栩雅保存起来,放眼配置文件类型,能够达到保存这种层级关系的文件很明显就可以用xml文件。至于怎么生成对象又是一个需要考虑的问题,由于我们要生成哪些对象是从配置文件中读取出来的,都出来后对象的表示肯定都是一个个字符串了,很显然我们不能使用new去生成对象了。java中什么技术可以使用字符串表示的类生成对象的,显然Class.forName(className)这个作为反射入口的方法是完全满足要求的。

  经过上面的分析是否对IoC的实现有了一个大概的想法。我们再联想使用spring框架的IoC的流程,我们看看如下码:

/**首先加载配置文件并解析放到ApplicationContext中*/
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
/**从ApplicationContext中get出来id为“beanId”的对象*/
ac.getBean("beanId");

可能有人在说自己在写web代码的时候没有写这种代码,但是你们集成spring到web中的是否配置过一个监听器Listener呢,然后启动tomcat那样的服务器的时候是不是会发现日志中或多或少会打印出一些生成对象的日志呢,难道这些代码他不能放到那个监听服务器启动的Listener中去吗。如果不信我的话可以去看看那个Listener的源码就知道了。退一万步讲,spring如果以后哪个版本采用了什么高大上的技术,让你看不到这些代码了。但是毫无疑问作为我们自己编写的IoC这种策略是完全没有任何问题的。

  上面分析了这么久,我们或多或少对于IoC框架该怎么实现都有了各自的方案了。下面我们结合spring框架的使用方式开始我们自己的IoC的框架之旅吧。首先spring框架抛开注解方式先不说,我们都是将需要生成的对象以bean的方式配置在xml文件中。说到xml我们就会想到xml格式的验证是需要dtd或者schema的,spring中xml文件的验证使用的是schema,由于schema的学习成本比dtd大,dtd又完全可以满足我们框架的要求,所以我们的IoC选用dtd文件作为校验xml的文件。结合spring配置文件中涉及到的标签元素我们定义的dtd文件如下:

<!--指定xml文档的根元素为beans,beans里面可以有多个子元素bean,仿照spring -->
<!ELEMENT beans (
bean*
)>
<!--指定根元素beans的两个属性,一个是延迟加载,一个是自动装配,默认为后面的值 -->
<!ATTLIST beans default-lazy-init (true | false) "false">
<!ATTLIST beans default-autowire (no | byName) "no">
<!-- 指定bean元素的子元素 -->
<!ELEMENT bean (
(constructor-arg | property)*
)>
<!-- 指定bean元素的属性值 ,这些属性和spring里面的类似-->
<!ATTLIST bean id CDATA #REQUIRED>
<!ATTLIST bean class CDATA #REQUIRED>
<!ATTLIST bean lazy-init (true | false | default) "default">
<!ATTLIST bean singleton (true | false) "true">
<!ATTLIST bean autowire (no | byName | default) "default">
<!-- 声明constructor-arg子元素 -->
<!ELEMENT constructor-arg (
(ref | value )
)>
<!-- 声明property元素的子元素 -->
<!ELEMENT property (
(ref | value | collection )?
)>
<!-- 指定collection元素的子元素 -->
<!ELEMENT collection (
(ref | value)+
)>
<!--声明collection的属性 -->
<!ATTLIST collection type CDATA #REQUIRED>
<!-- 声明property的属性 -->
<!ATTLIST property name CDATA #REQUIRED>
<!-- 声明property的属性 -->
<!ATTLIST value type CDATA #REQUIRED>
<!-- 声明ref元素 -->
<!ELEMENT ref EMPTY>
<!-- 声明ref的属性 -->
<!ATTLIST ref bean CDATA #REQUIRED>
<!-- 声明value元素 -->
<!ELEMENT value (#PCDATA)>

对于上面的dtd有了详细的注释,如果还是觉得看的吃力,小伙伴们可以自己网上找些dtd的资料话半小时完全可以轻松搞定。这里我们的重点不在这里。至于上面的dtd所校验下的xml文件的格式是个什么样子我们可以联想下spring的applicationContext.xml中的内容就能知道了。这边和那个大同小异。为了方便下面代码的讲解,我们说下这次项目的包结构

其中项目的根包为com.tear.ioc,bean包下面放一些项目需要使用的bean,dtd下放dtd文件,xml包下放处理xml文件的相关包或者类。下面的讲解全部是以这个包为基础进行的。

  dtd文件已经有了,但是我们怎么让这个dtd去约束xml呢?又怎么解析xml呢,原本的想法我的这个IoC框架不想去依赖任何jdk以外的包的,但是后来发现jdk自带的dom和sax解析xml用起来很繁琐,无奈下引用了一个目前使用比较多的dom4j.jar包,我们将获取xml文件的dom4j里面的document对象的代码放在xml.document包下:

下面我们定义一个Document对象的持有接口DocumentHolder

package com.tear.ioc.bean.xml.document;
import org.dom4j.Document;
public interface DocumentHolder {
/**
* 根据xml文件的路径得到dom4j里面的Document对象
* @param filePath
* @return
*/
public Document getDocument(String filePath);
}

持有接口的实现类XmlDocumentHolder如下

package com.tear.ioc.bean.xml.document;

import java.io.File;
import java.util.HashMap;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.io.SAXReader; public class XmlDocumentHolder implements DocumentHolder {
/**
* 由于可能配置多个配置文件所以定义一个Map类型的成员变量用配置文件的路径关联他们的Document对象
* Map的实际类型定义成了HashMap
*/
private Map<String, Document> documents = new HashMap<String, Document>(); /**
* 根据xml文件的路径得到dom4j里面的Document对象
* @param filePath
* @return
*/
@Override
public Document getDocument(String filePath) {
/**
* 通过xml文件的路径获取出Map里保存的Document对象
*/
Document doc = this.documents.get(filePath);
/**
* 如果根据xml文件的路径从Map中取出的Document对象为空,则调用本类里面定义的
* readDocument方法获得该路径所对应文件的Document对象后,在将路径和Document
* 对象这样一对信息保存到Map中去
*/
if (doc == null) {
//使用SAXReader来读取xml文件
this.documents.put(filePath, this.readDocument(filePath));
}
/**
* 返回Map中该xml文档路径所对应的Document对象
*/
return this.documents.get(filePath);
}
/**
* 根据文件的路径读取出Document对象,该方法是准备被下面的getDocument方法调用的
* 所以定义成了private
* @param filePath
* @return
*/
private Document readDocument(String filePath) {
try {
/**
* new一个带dtd验证的SaxReader对象
*/
SAXReader reader = new SAXReader(true);
/**
* 设置用来验证的dtd的输入源
*/
reader.setEntityResolver(new XmlEntityResolver());
/**
* 根据xml的路径读取出Document对象
*/
File xmlFile = new File(filePath);
Document document = reader.read(xmlFile);
return document;
} catch (Exception e) {
e.printStackTrace();
}
return null;
} }

使用指定dtd验证xml的resolver类XmlEntityResolver如下

package com.tear.ioc.bean.xml.document;

import java.io.IOException;
import java.io.InputStream;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* 自定义的XmlEntityResolver实现dom4j里的EntityResolver接口并实现里面的
* resolveEntity方法,来获得一个dtd的输入源
* @author rongdi
*/
public class XmlEntityResolver implements EntityResolver { @Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
/**
* 如果自己写的xml配置文件中引入dtd的时候publicId与"-//RONGDI//DTD BEAN//CN"相同
* 并且systemId与"http://www.cnblogs.com/rongdi/beans.dtd"相同,就从本地的相对项目的路径
* 寻找dtd,返回一个dtd的输入源,若果找不到该dtd就会尝试到对应的网址上寻找
*/
if ("-//RONGDI//DTD BEAN//CN".equals(publicId)&&"http://www.cnblogs.com/rongdi/beans.dtd".equals(systemId)) {
InputStream stream = this.getClass().
getResourceAsStream("/com/tear/ioc/bean/dtd/beans.dtd");
return new InputSource(stream);
}
return null;
}
}

注释在代码中写的很详细了这里就不多废话了。下面使用jUnit测试一下上面获取document的代码

在test包下建立包com.tear.ioc.xml.document包和resources包,resources.document包存放需要使用的xml文件等资源

resources.document包下测试获取document对象的正确文件xmlDocumentHolder.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//RONGDI//DTD BEAN//CN"
"http://www.cnblogs.com/rongdi/beans.dtd">
<beans>
<bean id="test" class="test"></bean>
</beans>

xml经不过dtd验证的文件xmlDocumentHolder2.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//RONGDI//DTD BEAN//CN"
"http://www.cnblogs.com/rongdi/beans.dtd">
<beans>
<bean id="test" ></bean>
</beans>

xml中publicId写错了的xml文件xmlDocumentHolder3.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//ABABAB//DTD BEAN//CN"
"http://www.cnblogs.com/rongdi/beans.dtd">
<beans>
<bean id="test" class="test"></bean>
</beans>

上面现阶段最主要关注的是DOCTYPE部分,下面的beans和bean部分属性中的内容对于现阶段随便写什么都可以。

单元测试代码如下

package com.tear.ioc.xml.document;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import com.tear.ioc.bean.xml.document.XmlDocumentHolder; public class XmlDocumentHolderTest {
private XmlDocumentHolder xmlHolder;
@Before
public void setUp() throws Exception {
xmlHolder = new XmlDocumentHolder();
} @After
public void tearDown() throws Exception {
xmlHolder = null;
}
//测试正常情况
@Test
public void testGetDocument1() {
String filePath = "test/resources/document/xmlDocumentHolder.xml";
//获得Document对象
Document doc1 = xmlHolder.getDocument(filePath);
//看是否为空,为空测试失败
assertNotNull(doc1);
//得到xml文档根元素
Element root = doc1.getRootElement();
//判断根元素是否为beans,不是beans测试失败
assertEquals(root.getName(), "beans");
//再获取一次Document对象,看是否一致
Document doc2 = xmlHolder.getDocument(filePath);
System.out.println(doc1);
System.out.println(doc1);
assertEquals(doc1, doc2);
}
//测试一个读取DTD验证不合格的xml文件看是否抛出异常
@Test(expected = DocumentException.class)
public void testGetDocument2(){
/*
* 定义一个dtd验证不合格的xml文件,该xml文件的bean元素id和class是必须,
* 但是少了一个class应该抛出异常
*/
String filePath = "test/resources/document/xmlDocumentHolder2.xml";
//获得Document对象
Document doc = xmlHolder.getDocument(filePath);
}
//测试一个读取不到DTD的情况(DTD里面的publicId或systemId写错了无法再本地获取dtd就会
//尝试到网上下载,但是自定义的网站根本不存在就会报错了)
@Test(expected = DocumentException.class)
public void testGetDocument3() throws DocumentException{
/*
* 定义一个dtd验证不合格的xml文件,该xml文件的bean元素id和class是必须,
* 但是少了一个class应该抛出异常
*/
String filePath = "test/resources/document/xmlDocumentHolder3.xml";
//获得Document对象
Document doc = xmlHolder.getDocument(filePath);
}
}

  

  好了对于IoC的开始部分,定义dtd即如何使用dtd校验xml的代码部分已经结束了。下次再见。 

  源码百度云地址http://pan.baidu.com/s/1sjHMT33 如果有问题可以留言我会在看到后第一时间回复

自己动手编写IOC框架(一)的更多相关文章

  1. 自己动手编写IOC框架(四)

    终于到了激动人心的时刻了,首先感谢小伙伴们的阅读,如果能多点评论,多点探讨就更好了,没有交流让我觉得我写的东西只有标题有点价值,内容只是在浪费大家的时间.为了泪滴下周能写下一个框架orm,请小伙伴们能 ...

  2. 自己动手编写IOC框架(三)

    刚写博客浏览量第一天就有1000多人次,给了我很大的鼓舞决定熬夜再写一篇.对于前两篇来说无非就是使用dtd验证xml,然后解析xml,和IOC的核心还是差的很远,相信很多小伙伴们都感觉看得不过瘾了,这 ...

  3. 自己动手编写IOC框架(二)

    万事开头难,上篇已经起了一个头,之后的事情相对就简单了.上次定义了框架所需的dtd也就是规定了xml中该怎么写,有哪些元素.并且我们也让dtd和xml绑定在了一起,使dtd对xml的格式进行校验,并且 ...

  4. 自己动手编写spring IOC源码

    前言:对于spring IOC概念不是很了解的朋友可以阅读我上一篇博客--轻松理解spring IOC(这两篇博客也是由于我的个人原因导致现在才发布,惭愧啊).通过这篇博客的理解之后,相信大家会对sp ...

  5. 国人编写的开源 .net Ioc 框架——My.Ioc 简介

    My.Ioc 是作者开发的一款开源 IoC/DI 框架,下载地址在此处.它具有下面一些特点: 高效 在实现手段上,My.Ioc 通过使用泛型.缓存.动态生成代码.延迟注册.尽量使用抽象类而非接口等方式 ...

  6. 自己动手写Spring框架--IOC、MVC

    对于一名Java开发人员,我相信没有人不知道 Spring 框架,而且也能够轻松就说出 Spring 的特性-- IOC.MVC.AOP.ORM(batis). 下面我想简单介绍一下我写的轻量级的 S ...

  7. OWIN系列之自己动手编写中间件

    一.前言 1.基于OWIN的项目摆脱System.Web束缚脱颖而出,轻量级+跨平台,使得ASP.NET应用程序只需依赖这个抽象接口,不用关心所运行的Web服务器. 2.OWIN.dll介绍 使用反编 ...

  8. 各大主流.Net的IOC框架性能测试比较

    Autofac下载地址:http://code.google.com/p/autofac/ Castle Windsor下载地址:http://sourceforge.net/projects/cas ...

  9. 【Android开发经验】来,咱们自己写一个Android的IOC框架!

    到眼下位置.afinal开发框架也是用了好几个月了,还记得第一次使用凝视完毕控件的初始化和事件绑定的时候,当时的心情是多么的兴奋- -代码居然能够这样写!然后随着不断的学习,也慢慢的对IOC框架和注解 ...

随机推荐

  1. LaunchScreen.storyboard 换图的问题

    之前设置了`LaunchScreen.storyboard`,在这个storyboard中加了一个imageView,里面设置了一张图片launch.png,今天需要更换这个启动图片,我就直接去工程里 ...

  2. Django+Nginx+uWSGI部署

    一.介绍 Django的部署可以有多种方式,采用nginx+uwsgi的方式是最常见的一种方式.在这种方式中,将nginx作为服务器前端,接收WEB的所有请求,统一管理请求.nginx把所有静态请求自 ...

  3. #define is unsafe

    #define is unsafe Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...

  4. 磁盘管理 之 parted命令添加swap,文件系统

    第1章 磁盘管理 1.1 必须要了解的. 1.1.1 ps aux 命令中 RSS 与VSZ的含义 rss 进程占用的物理内存的大小 单位:kb : vsz 进程占用的虚拟的内存大小(物理内存+swa ...

  5. Python 读取某个目录下的文件

    读取某个目录下的文件,如'/Users/test/test_kmls'目录下有test1.txt.test2.txt. 第一种方法读出的all_files是test1.txt.test2.txt im ...

  6. ML技术 - 特征选择

    1. 决策树中的特征选择 分类决策树是一种描述对实例进行分类的树型结构,决策树学习本质上就是从训练数据集中归纳出一组分类规则,而二叉决策树类似于if-else规则.决策树的构建也是非常的简单,首先依据 ...

  7. [Bayesian] “我是bayesian我怕谁”系列 - Gaussian Process

    科班出身,贝叶斯护体,正本清源,故拿”九阳神功“自比,而非邪气十足的”九阴真经“: 现在看来,此前的八层功力都为这第九层作基础: 本系列第九篇,助/祝你早日hold住神功第九重,加入血统纯正的人工智能 ...

  8. openstack学习心得:keystone 常用命令(M版)

    查看用户列表 openstack user list 查看用户具体信息 usage: openstack user show [-h] [-f {html,json,json,shell,table, ...

  9. MVC 页面静态化

    最近工作需要,实现页面静态化,以前在ASP时代,都是FSO自己手动生成的. 新时代,MVC了,当然也要新技术,网上一搜,找到一种解决方案,是基于MVC3的,实现原理是通过mvc提供的过滤器扩展点实现页 ...

  10. [转载] 谷歌技术"三宝"之MapReduce

    转载自http://blog.csdn.net/opennaive/article/details/7514146 江湖传说永流传:谷歌技术有"三宝",GFS.MapReduce和 ...