题外话
重复造轮子并不是件不好的事,作为学习我觉得造几个轮子还是很有必要的,光看不练假把式,况且看完后容易忘掉,又要重新看,还不如动手写几行,一来加深印象,二来在coding过程中还能学到不少相关的知识,比如在实现自己IOC的时候,除了深入理解其原理,还能了解诸如xml解析,编写dtd文档保证xml格式的有效性等等。最后写篇博文总结并梳理一下思绪,好了下面正式开始。
首先新建一个Person类
public class Person {
private String name;
private Integer age;
//省略setter,getter方法
}
然后再建一个PersonService
public class PersonService {
private Person person;
public void info(){
System.out.println("My name's "+person.getName()+" , I'm "+person.getAge()+" years old!");
}
public void setPerson(Person person) {
this.person = person;
}
}
和Spring一样,这里采用setter方法实现依赖注入,使用XML文件来保存对象之间的依赖关系
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="person" class="com.firefly.core.Person">
<property name="name" value="Jack"/>
<property name="age" value="12"/>
</bean>
<bean id="personService" class="com.firefly.core.PersonService">
<property name="person" ref="person"/>
</bean>
</beans>
一开始只实现最最基础的部分, 即读取、解析配置文件,然后利用反射实现对象的依赖注入。
我们先不管怎么实现解析xml,也不管怎么依赖注入。首先回顾一下Spring当中配置好xml后怎么来使用,一般都是这样
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("firefly.xml");
PersonService personService = (PersonService)applicationContext.getBean("personService");
ok,那么我们也新建一个ApplicationContext接口,里面有个getBean方法,然后新建一个FileSystemXmlApplicationContext来实现这个接口
/**
* IOC容器基本接口
* @author 杰然不同
* @date 2010-11-26
* @Description: 定义IOC容器基本规范
* @Version 1.0
*/
public interface ApplicationContext {
public Object getBean(String name);
}
/**
* 容器接口实现
* @author 杰然不同
* @date 2010-11-29
* @Version 1.0
*/
public class FileSystemXmlApplicationContext extends AbstractApplicationContext{
public FileSystemXmlApplicationContext(String fileName) {
super.reader = new XmlBeanDefinitionReader(fileName);
// 启动容器初始化
refresh();
}
}
细心的朋友会看到FileSystemXmlApplicationContext并没有直接实现ApplicationContext,而是继承了名为AbstractApplicationContext
的抽象类,我们先来看下这是个什么类
/**
* IOC容器的具体实现
* @author 杰然不同
* @date 2010-12-5
* @Version 1.0
*/
public abstract class AbstractApplicationContext implements ApplicationContext {
protected Map<String,BeanDefinition> beansDefinitionMap = new HashMap<String, BeanDefinition>();
protected Map<String, Object> beansMap = new HashMap<String, Object>();
protected BeanDefinitionReader reader;
/**
* 创建Bean
* @Date 2010-11-30
* @param beanName
* @return 具体Bean实例
*/
protected Object createBean(String beanName) {
Object beanobj = this.beansMap.get(beanName);
if(beanobj != null)
return beanobj;
Class<?> clazz = null;
Object obj = null;
BeanDefinition beanDefinitionan = (BeanDefinition)beansDefinitionMap.get(beanName);
if(beanDefinitionan != null){
try {
clazz = Class.forName(beanDefinitionan.getClassName());
obj = clazz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}else{
}
// 依赖注入
setProperties(obj, beanDefinitionan.getProperties());
beansMap.put(beanName, obj);
return obj;
}
/**
* 获取Bean
*/
public Object getBean(String name){
return createBean(name);
}
/**
* 使用set方法注入值
*/
private Object setProperties(Object obj, Map<String, Object> properties) {
Class<?> clazz = obj.getClass();
try {
Method[] methods = clazz.getMethods();
for(Entry<String, Object> entry : properties.entrySet()){
String key = entry.getKey();
Object value = entry.getValue();
for(Method m : methods){
// 取出所有set方法并且只有一个参数
String methodName = m.getName();
Class<?>[] argsType = m.getParameterTypes();
if(methodName.startsWith("set") && argsType.length == 1){
String tempName = methodName.substring(3, methodName.length()).toLowerCase();
if(tempName.equals(key)){
setFieldValue(argsType[0].getName(),(String)value,m,obj);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
private void setFieldValue(String className, String value, Method m,
Object obj) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
if (className.equals("byte"))
m.invoke(obj, Byte.parseByte(value));
else if (className.equals("short"))
m.invoke(obj, Short.parseShort(value));
else if (className.equals("int"))
m.invoke(obj, Integer.parseInt(value));
else if (className.equals("long"))
m.invoke(obj, Long.parseLong(value));
else if (className.equals("float"))
m.invoke(obj, Float.parseFloat(value));
else if (className.equals("double"))
m.invoke(obj, Double.parseDouble(value));
else if (className.equals("boolean"))
m.invoke(obj, Boolean.parseBoolean(value));
else if (className.equals("java.lang.Byte"))
m.invoke(obj, new Byte(value));
else if (className.equals("java.lang.Short"))
m.invoke(obj, new Short(value));
else if (className.equals("java.lang.Integer"))
m.invoke(obj, new Integer(value));
else if (className.equals("java.lang.Long"))
m.invoke(obj, new Long(value));
else if (className.equals("java.lang.Float"))
m.invoke(obj, new Float(value));
else if (className.equals("java.lang.Double"))
m.invoke(obj, new Double(value));
else if (className.equals("java.lang.String"))
m.invoke(obj, new String(value));
else if (className.equals("java.lang.Boolean"))
m.invoke(obj, new Boolean(value));
else
m.invoke(obj, createBean(value));
}
/**
* IOC容器初始化入口
* @Date 2010-11-29
*/
public void refresh(){
beansDefinitionMap = reader.loadBeanDefinitions();
}
}
原来IOC的具体实现是在这个类中,之所以这么做也是为了能有更好的扩展性。在这里我有必要说一下springioc工作的流程。IOC容器需要进行初始化,粗略的说就是解析配置文件将bean信息缓存到一个HashMap中,在这个过程中有专门的读取器对资源进行定位、解析、注册,这也是解耦的一种体现。于是在AbstractApplicationContext 中声明一个读取器BeanDefinitionReader,然后在其子类指明是由哪种读取器来读取,
此处自然我们需要一个解析xml的读取器super.reader = new XmlBeanDefinitionReader(fileName);
在FileSystemXmlApplicationContext中只做两件事,一个是将配置文件名给读取器,配置文件放在classpath下面,
然后调用refresh()启动IOC的初始化
我们顺着refresh()看下去,读取器调用loadBeanDefinitions()方法开始处理配置文件,下面是读取器的相关内容
BeanDefinition用来表示单个Bean
public class BeanDefinition {
// Bean的id
private String id;
// Bean的class
private String className;
// Bean的属性集合
private Map<String, Object> properties = new HashMap<String, Object>();
// 省略getter,setter方法
}
读取、解析配置文件
1.创建读取配置文件通用接口BeanDefinitionReader
/**
* 加载配置文件接口
* @author 杰然不同
* @date 2010-11-28
* @Version 1.0
*/
public interface BeanDefinitionReader {
/**
* 读取配置文件中的信息
* @Date 2010-11-29
* @param fileName 文件名
* @return map
*/
public abstract Map<String, Bean> loadBeanDefinitions();
}
2.创建XmlBeanDefinitionReader实现以上接口,看名字很容易知道是读取XML形式的配置文件,今后可以扩展其他形式,只需实现上面的接口即可。
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
private final String fileName;
protected static Logger log = Logger.getLogger(XmlBeanDefinitionReader.class.getName());
public XmlBeanDefinitionReader(String fileName) {
this.fileName = fileName;
}
/**
* 读取配置文件,将Bean信息存入HashMap中
* @Date 2010-12-5
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, BeanDefinition> loadBeanDefinitions(){
Map<String, BeanDefinition> beanDefinitionsMap = new HashMap<String, BeanDefinition>();
Document doc = null;
// 获得Xml文档对象
try {
log.info("Get XML Document");
doc = readDocument(this.fileName);
} catch (DocumentException e) {
e.printStackTrace();
return null;
}
// 获得根节点
List<Element> beans = doc.getRootElement().elements("bean");
// 遍历所有跟节点
for(Element e : beans){
BeanDefinition beanDefinitionan = new BeanDefinition();
String id = e.attributeValue("id");
String className = e.attributeValue("class");
beanDefinitionan.setId(id);
beanDefinitionan.setClassName(className);
// 获得Bean中所有property
List<Element> propertiesList = e.elements("property");
// 遍历所有property
for(Element e1 : propertiesList){
String name = e1.attributeValue("name");
// 如果是普通赋值形式
if(e1.attribute("value") != null)
beanDefinitionan.getProperties().put(name, e1.attributeValue("value"));
// 如果是引用另一个Bean形式
if(e1.attribute("ref") != null)
beanDefinitionan.getProperties().put(name, e1.attributeValue("ref"));
}
beanDefinitionsMap.put(id, beanDefinitionan);
}
return beanDefinitionsMap;
}
/**
* 根据文件读取Document
* @Date 2010-11-28
* @param filePath
* @return 文档对象
* @throws DocumentException
*/
private Document readDocument(String filePath) throws DocumentException{
// 获得带上classpath路径的文件路径
filePath = Thread.currentThread().getContextClassLoader().getResource(
filePath).getPath().substring(1);
log.info("Loading XML bean definitions from file [" + filePath + "]");
//使用SAXReader来读取xml文件
SAXReader reader = new SAXReader();
Document doc = null;
doc = reader.read(new File(filePath));
return doc;
}
}
读取器经过一系列处理后,将xml中所有的内容缓存到Map<String, BeanDefinition>中,到此为止IOC容器初始化完毕。Bean的依赖注入在第一次getBean的时候触发。
我们回到AbstractApplicationContext中看getBean方法,它直接调用了createBean新建Bean,顺着方法看下去,可以看到是利用了java反射机制来实例化Bean对象和它的属性对象,然后同样缓存到HashMap中。
到这里一个简单IOC完成了,我们写一个测试
public class ApplicationContextTest extends TestCase {
@Test
public void testGetBean(){
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("firefly.xml");
PersonService personService = (PersonService)applicationContext.getBean("personService");
personService.info();
Person person = (Person)applicationContext.getBean("person");
System.out.println(person.getName());
}
}
输出:
My name's Jack , I'm 12 years old!
Jack
下一节我们进行第一次重构!!!
ps:附件是重构前的源码,用maven构建的
分享到:
相关推荐
这个“一个简单的模仿spring的ioc实现”项目旨在帮助开发者理解Spring Ioc的核心概念,并通过自己的代码实现来加深对这一机制的理解。 首先,Spring Ioc的主要目标是解耦应用程序组件。它通过管理对象的生命周期和...
本项目"手写一个SpringIoc容器"旨在模仿Spring的IOC(Inversion of Control,控制反转)功能,帮助开发者深入理解Spring的工作原理,提升对依赖注入(Dependency Injection)模式的认识。 在实现自定义的Spring IOC...
Spring Ioc(Inversion of Control,控制反转)是Spring框架的核心特性之一,它改变了传统应用程序中对象的创建和管理方式。在传统的软件设计中,对象的创建和依赖关系的维护通常由代码自身来完成,而在Spring Ioc中...
在IT行业中,依赖注入(IOC,Inversion of Control)是一种设计模式,它使得应用程序的组件之间解耦,提高了代码的可测试性和可维护性。在这个“自己实现ioc实例demo”中,我们将探讨如何通过XPath解析XML文件来实现...
IoC(Inversion of Control)是 Spring 框架中的一种设计模式,它的主要思想是将对象的创建和管理交给容器,从而解耦合对象之间的依赖关系。今天,我们将详细解析 IoC 的优点和缺点。 优点 1. 简化对象的创建:IoC ...
Spring IOC(Inversion of Control,控制反转)是一种软件设计模式,它将对象的创建和管理交给容器,实现了对象之间的解耦合。 SpringIOC是Spring Framework中的核心组件之一,负责管理应用程序中的对象、依赖关系和...
我们从一个简单的容器开始,一步步的重构,最后实现一个基本的Spring框架的雏形,为了帮助我们更加深入的理解Spring的IoC的原理和源码。 详细内容见博文: 【SSH进阶之路】一步步重构容器实现Spring的IoC——从一个...
"图片转IOC图标工具"正是一款专为开发者设计的实用工具,旨在帮助他们将普通的图片转换为适用于iOS平台的.IOC图标格式。 .IOC(Image Asset Catalog)是苹果iOS开发中的图像资源格式,主要用于存储不同尺寸和状态的...
IOC(Inversion of Control,控制反转)模式是一种软件设计原则,它在面向对象编程中用于解耦组件之间的依赖关系。C#中实现IOC的一种常见方式是通过依赖注入(Dependency Injection,DI)。在这个“IOC模式 c#经典...
IoC,全称为Inversion of Control,即控制反转,是软件设计中的一个重要概念。这个概念在Java和.NET等面向对象编程语言中尤其常见,主要应用于依赖注入(Dependency Injection,简称DI)的设计模式中。IoC的核心思想...
1:创建一个空的Java工程 2:将lib目录下的包与demo下src目录放入工程 3:将编译demo代码 4:运行文件: demo\src\org\jmin\test\...5:运行文件:demo\src\org\jmin\test\swing\SwingAppStarter.java 一个ioc应用XML案例
在这个“一个简单的spring IOC演示demo”中,我们将深入探讨Spring IOC的工作原理和如何在实践中运用。 首先,我们需要理解什么是IOC。在传统的Java程序中,对象的创建和依赖关系通常由代码直接管理,而在Spring ...
标题中的“雷赛IOC0640.rar”指的是雷赛智能公司的一款名为IOC0640的工业控制产品,该产品通常用于自动化控制系统中,提供高效、稳定的输入/输出(I/O)管理。这个压缩包可能包含了关于该产品的详细资料和技术文档。...
本文将深入探讨一个简单的IOC容器实现,帮助我们理解DI的基本原理。 首先,理解IOC的概念至关重要。在传统的编程模式中,对象通常自行创建依赖的对象,这导致了代码之间的紧密耦合。而在IOC模式下,容器负责管理...
**IOC(Inversion of Control)**,即控制反转,是一种设计模式,它的核心思想是将对象的创建和依赖关系的管理从应用代码中解耦出来,交给一个专门的容器来处理。这种模式使得代码更加灵活,降低了模块间的耦合度,...
标题 "手动实现一个IOC容器.zip" 提到的主题是关于如何从零开始构建自己的依赖注入(Dependency Injection,简称DI)容器,这在Java开发中尤为重要,特别是对于Spring框架的理解和学习。IOC容器是Spring的核心功能之...
- 虽然不是IOC直接的一部分,但AOP是Spring框架的另一个重要特性,常与IOC一起使用。它允许我们定义横切关注点,如日志、事务管理等,以解耦业务逻辑。 10. **测试支持** - Spring提供了测试支持,如@Test注解的...
控制反转(IOC,Inversion of Control)是一种设计模式,它在软件工程中被广泛应用,特别是在Java开发中。IoC的核心思想是将对象的创建和管理权交给一个外部容器,而不是让对象自行创建和管理依赖。这有助于降低耦合...