`
bolide74
  • 浏览: 83163 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring温故知新(四)用HashMap写一个自己的Spring IoC简易容器吧!(改进一)

阅读更多
在介绍Spring的IoC容器之前,我首先要给大家介绍一下我刚刚花了仅仅10分钟就写出来的一个用HashMap而不是用XML来配置的IoC容器,然后剖析这段代码,彻底的撕碎初学者对于IoC概念的恐惧感(好像有点夸张了,哈哈!)

引用
以下示例只为了解释原理而不是写一个成熟的IoC容器,因此尽可能的把与主题无关的代码给省略掉了,希望那些准备写自己的成熟容器的同学别学这个示例



首先还是亲切的Robot类:
package com.iteye.bolide74.action;

public class RobotBean {
	public String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void Speak(String msg) {
		System.out.println(msg + ",我是" + this.name);
	}
}

为了解释,这个类只有一个属性。使用了set注入
这里我把类名叫做RobotBean,这是为了提前让大家知道Bean这么一个东西,告诉大家这个就是一个Bean,而关于Bean到底代表着什么意思,那么下回我就会介绍了。


下面就是简易IoC容器的代码了:
package com.iteye.bolide74.action;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

public class MySpringIoC {
	public HashMap<String, HashMap<String, Object>> configLikeXML = new HashMap<String, HashMap<String, Object>>();

	public MySpringIoC() {
		HashMap<String, Object> robotBeanConfig = new HashMap<String, Object>();
		robotBeanConfig.put("class", "com.iteye.bolide74.action.RobotBean");
		robotBeanConfig.put("name", "Robot1");
		configLikeXML.put("Robot", robotBeanConfig);
	}

	public Object getBean(String beanID) {
		// 获取hashMap配置内容
		HashMap<String, Object> beanConfig = this.configLikeXML.get(beanID);
		// 获取完整类名
		String className = (String) beanConfig.get("class");
		Class<?> bean = null;
		try {
			// 获取类类型实例
			bean = Class.forName(className);
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		}
		Object instance = null;
		try {
			// 获取这个类的一个实例
			instance = bean.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		for (Field field : bean.getDeclaredFields()) {
			// 获取属性名
			String fieldName = field.getName();
			// 在hashMap获取属性要注入的值(这里为了方便把类型写死了)
			String fieldValue = (String) beanConfig.get(fieldName);
			Method method = null;
			try {
				// 获取到这个属性的set方法(这里为了方便把类型写死了)
				method = bean.getDeclaredMethod(
						"set" + fieldName.substring(0, 1).toUpperCase()
								+ fieldName.substring(1),
						new Class[] { String.class });
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
			try {
				// 调用set方法
				method.invoke(instance, new Object[] { fieldValue });
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return instance;
	}
}

然后是实现类的应用代码:
package com.iteye.bolide74.tester;

import com.iteye.bolide74.action.MySpringIoC;
import com.iteye.bolide74.action.RobotBean;

public class MySpringIoCTester {
	public static void main(String[] args) {
		MySpringIoC beanFactory = new MySpringIoC();
		RobotBean robotBean = (RobotBean) beanFactory.getBean("Robot");
		robotBean.Speak("Hello,world!");
	}
}


      这时候大家可以用这一段实现类代码和最初的第一篇HelloWorld的代码比较一下,是不是特别神似?http://bolide74.iteye.com/blog/993248只不过Spring的HelloWorld在实例化ApplicationContext这个IoC容器的时候还需要传入一个XML配置文件的路径,而我这里直接省略了。
      这是为什么呢?那是因为我没有用XML来配置IoC容器,而是直接在IoC容器里建了一个HashMap来当作配置文件,一样能起到简单的配置功能。

      这个HashMap结构很简单,Key就是RobotBean的ID,它可以是任意的字符串,不需要跟RobotBean的类名相关;而Value就是RobotBean的完整类名了,包含了RobotBean的包名和类名,这个就不能乱写了哈!
      当然以后如果你又做了一个PeopleBean想要把它也加进这个IoC容器里来管理,那也是同理,新建一个HashMap配置容器


那么接下来就是要介绍IoC容器的真正核心所在了,如何根据这个“配置容器”来获取到一个实例:getBean()!
其实要是清楚JAVA的反射机制的同学,不用我介绍就直接明白了,但是如果不清楚什么叫反射的同学,我这里用最通俗但是可以不太准确的说法来介绍:反射,就是根据一个类的完整类名(包名+类名),来把这个类实例化。注意,实例化出来的类型是一个类,叫做Class类类型。也就是是说Robot机器人不再是一个称呼,一个代号,而是切切实实存在的看的见摸的着一个种类(要还是不能理解,你就暂时把它想像成机器人工厂类的一个实例吧...)。

然后获取到了这么一个实例类以后,就可以直接调用一个newInstance()方法,也就是让这个实例类自动的产生一个这个类的实例对象给我们,就像是机器人工厂实例生产出了一个机器人。

这时候我们可以看到,getBean这个方法的返回类型是Object,也就是一个超类型。java也好C#也好都有一个特性,就是所有的类都是Object的子类,所以Object可以代表任何类。
这样一来的话,在应用代码中调用getBean()方法的时候,只要把返回类型强制转换为调用getBean方法的引用对象的类型就可以了。


以上内容,就基本上剖析了IoC的实现原理,当然真正Spring的IoC框架功能更强大,我这个在依赖注入的时候还是直接写死的类型,其实还可以自动判断的。但对于描述IoC容器实现原理没太大意义,这里就省略了。


最后我要告诉大家的是,以上内容纯属猜测,如有雷同那真是太巧啦! 
我其实没看过Spring的源码哈,都是猜的,我想应该八九不离十,万一错了,千万别回头来找我算账,概不负责哦~   


下一篇:Spring温故知新(五)Spring的Bean和IoC 容器 http://bolide74.iteye.com/blog/1004086
上一篇:Spring温故知新(三)singleton单例模式 http://bolide74.iteye.com/blog/1001630








10
4
分享到:
评论
10 楼 bolide74 2011-05-10  
yuanyu5237 写道
楼主,不好意思,我有点不明白,为什么要在robotBeanConfig.put("name", "Robot1");  这里传入RobotBean的name值,我们在xml里只配置了bean的id和class,并没有把bean中属性的值也配进去,属性值(name的值)应该是再客户端测试类中获得了bean的实例后,用户自己设置的吧,希望楼主能解释下,谢谢
写的很不错,加你关注了

嗯,会有这个疑问,可能是由于你还对Spring的依赖注入还不够了解。事实上如果你从第二章开始看起的话你就会发现,"在客户端测试类中获得bean实例后用户自己设置的"是msg这个值,也就是Speak方法的形参。
而name值是RobotBean类的一个成员属性,而不是方法的形参,因此是需要在获取RobotBean的实例之前就要在配置文件中注入,这样才能达到控制和管理实例的作用。
可以参照与前面几章的简单工厂方法来加强理解含义,如果不事先注入这个成员属性,那所谓的简单工厂方法也就没有了它存在的意义,而IoC的最大优势也就体现不出来了。
所以事实上,"我们在xml里只配置了bean的id和class,并没有把bean中属性的值也配进去"这个理解是错误的,xml里配置bean的id和class只是基础作用,更大的作用就是为了注入bean的属性
9 楼 yuanyu5237 2011-05-10  
楼主,不好意思,我有点不明白,为什么要在robotBeanConfig.put("name", "Robot1");  这里传入RobotBean的name值,我们在xml里只配置了bean的id和class,并没有把bean中属性的值也配进去,属性值(name的值)应该是再客户端测试类中获得了bean的实例后,用户自己设置的吧,希望楼主能解释下,谢谢
写的很不错,加你关注了
8 楼 2xusi 2011-05-05  
挺 好 的
7 楼 bolide74 2011-04-29  
C.T 写道
你的Map容器如果是这样处理的话,应该至少加多一个方法,可以将你的映射放入你的

 HashMap<String, Object> robotBeanConfig = new HashMap<String, Object>(); 

里面的robotBeanConfig中吧。这样方便点。


是的,不过我的初衷只为了解释原理而不是写一个成熟的IoC容器,因此尽可能的把与主题无关的代码给省略掉了,希望那些准备写自己的成熟容器的同学别学这个示例啊!
6 楼 C.T 2011-04-29  
你的Map容器如果是这样处理的话,应该至少加多一个方法,可以将你的映射放入你的

 HashMap<String, Object> robotBeanConfig = new HashMap<String, Object>(); 

里面的robotBeanConfig中吧。这样方便点。
5 楼 bolide74 2011-04-28  
爪哇岛岛主 写道
无非就是一个填值的问题,我大二的时候写了一个很垃圾的,不过现在都无所谓写了,很多框架做的非常出色。。。

其实咱做编程的一向都是会的不难,难的就是不会,只要明白了原理以后就都很简单了
4 楼 bolide74 2011-04-28  
竹隐江南 写道
bolide74 写道
消灭0回复!   莫非现在都只对单纯的设计模式感兴趣么。。。

哈哈,看LZ很牛逼,看了几篇文章,嗯,非常不错,记得你说你学java是直接从spring开始扣得,我很佩服,我本来是搞java的后来干了半年,现在外包,干的乱七八糟,想复习无处下手,索性看看你得文章,很不错,像做了至少3年的人写的。得多学习啊

其实也是因为我有C#的基础,要不然也啃不下来啊
3 楼 爪哇岛岛主 2011-04-28  
无非就是一个填值的问题,我大二的时候写了一个很垃圾的,不过现在都无所谓写了,很多框架做的非常出色。。。
package util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import pojo.Admin;

public class FillPojo<T> {

	static Admin admin = null;

	@SuppressWarnings("unchecked")
	public T fillPojo(Class<T> obj,Map<String,Object> mapData) throws Exception{
		Map.Entry<String, Object> param = null;
		Iterator<Map.Entry<String, Object>> it = mapData.entrySet().iterator();
		T pojo = null;
		Class<?> myClass = null;
		String propertyName = null;
		Object propertyValue = null;
		Field field = null;
		Method method = null;
		StringBuffer methodName = null;

		myClass = Class.forName(obj.getName());
		/**
		 * 获取POJO实例
		 */
		pojo = (T) myClass.newInstance();


		while(it.hasNext()){
			param = (Entry<String, Object>)it.next();
			/**
			 * 获取属性的名称和值
			 */
			propertyName = param.getKey();
			propertyValue = param.getValue();
			/**
			 * 获取属性的setter方法名称
			 */
			methodName = new StringBuffer();
			methodName.append("set");
			methodName.append(propertyName.substring(0, 1).toUpperCase());
			methodName.append(propertyName.substring(1));

			field = myClass.getDeclaredField(propertyName);

			method = myClass.getMethod(methodName.toString(),field.getType());

			method.invoke(pojo,propertyValue);

		}

		return pojo;
	}

}
2 楼 竹隐江南 2011-04-28  
bolide74 写道
消灭0回复!   莫非现在都只对单纯的设计模式感兴趣么。。。

哈哈,看LZ很牛逼,看了几篇文章,嗯,非常不错,记得你说你学java是直接从spring开始扣得,我很佩服,我本来是搞java的后来干了半年,现在外包,干的乱七八糟,想复习无处下手,索性看看你得文章,很不错,像做了至少3年的人写的。得多学习啊
1 楼 bolide74 2011-04-14  
消灭0回复!   莫非现在都只对单纯的设计模式感兴趣么。。。

相关推荐

Global site tag (gtag.js) - Google Analytics