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

基本IOC尝试

 
阅读更多

近两个月一直被肠胃病折磨着,痛苦了好久,这段时间稍微好点了。身体不好,技术也就放下了。搞技术的朋友们啊,要保重好自己的身体啊,年轻并不代表可以挥霍健康。

好了,废话少说,今天,这几天尝试自己写了一点点IOC的实现,当然只是很基本的属性注入的,对象的那些还没有去处理。但起码把自己一直以来想要深入理解一个思想的想法付诸行动了。

搞JAVA的朋友肯定都知道IOC是啥来的,如果不知道的,看看这个http://baike.baidu.com/view/146665.htm#sub6386770。简要的说一下就是把对象之间的关联交给容器来处理,而不是我们以前代码那样,直接来set或new一个。

知道了它是什么来的,我们肯定很希望知道它是怎么实现的,用过spring的朋友当然都知道。拿个xml文件,配置一下,然后加载,再getBean,一切Ok,啥都不用管,或者web中用contextLoaderListener或者DispatcherServlet都可以搞定,但停留在使用的层面上,显然不是我们想要的。

我们知道容器只是帮我们处理了一些依赖,如set,new等等,如此而已(听起来很简单,但有很多东西需要考虑的)。

下面我们就一起来看看,实现一个基本的可以注入属性的IOC容器,我们先不管注入依赖对象。

1)要注入属性,我们一般是通过调用setXXX方法,这里我们也是这样的(暂时不考虑构造函数注入),我们通过反射来调用相应的set方法。

我们这里的配置载体还是最流行的XML了,注解也很流行,但暂时还是不弄了。

我们有一个Bean来保存所有bean的配置属性等,就如同spring的BeanDefinitionHolder:

package com.shun.bean;

import java.util.List;
import java.util.Map;

public class BeanDefinition {

	private String name;//保存bean的id
	private String type;//保存class的值
	private List<Map<String,Object>> properties;//保存property标签的相关配置
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public List<Map<String,Object>> getProperties() {
		return properties;
	}
	public void setProperties(List<Map<String,Object>> properties) {
		this.properties = properties;
	}
	
}

  当然,很简陋,只保存了相当于bean的id,class还有一系列property标签的配置。但已经足够我们来学习基本的属性注入了。

2)我们是通过反射来调用相应的类的属性的set方法,当然这时属性的首字母会变大写,如属性为name,则set方法是setName,我写了一个工具类来处理首字母大写:

package com.shun.utils;

public class DataUtils {

	/**
	 * 将传入的字符串首字母大写
	 * @param str
	 * @return
	 */
	public static String capitalize(String str) {
		return str.substring(0,1).toUpperCase()+str.substring(1);
	}
	
}

  3)然后最主要的当然是数我们的IOC处理类了MyIocProcessor:

首先我们来看看读取配置文件的方法:

	/**
	 * 读取配置文件,并把配置文件中的相关配置保存到beanDefinitionList中
	 * @param configPath
	 * @return
	 */
	public List<BeanDefinition> processConfig(String configPath){
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		List<BeanDefinition> beanDefinitionList = new ArrayList<BeanDefinition>();
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(new File(configPath));
			NodeList nodeList = doc.getElementsByTagName("bean");
			//分析处理每一个bean标签
			for (int i = 0; i < nodeList.getLength(); i ++) {
				Node node = nodeList.item(i);
				NamedNodeMap attributes = node.getAttributes();
				BeanDefinition beanDefinition = new BeanDefinition();
				beanDefinition.setName(attributes.getNamedItem("id").getNodeValue());//取得bean的id
				beanDefinition.setType(attributes.getNamedItem("class").getNodeValue());//取得class
				
				NodeList properties = node.getChildNodes();
				List<Map<String,Object>> propertyMapList = new ArrayList<Map<String,Object>>();
				//处理bean标签下的property标签
				for (int j = 0; j < properties.getLength(); j ++) {
					Node property = properties.item(j);
					Map<String,Object> propertyMap = new HashMap<String,Object>();
					if (property instanceof Element) {
						NamedNodeMap propertyAttributes = property.getAttributes();
						propertyMap.put("name",propertyAttributes.getNamedItem("name").getNodeValue());
						propertyMap.put("value",propertyAttributes.getNamedItem("value").getNodeValue());
						propertyMapList.add(propertyMap);
					}
				}
				beanDefinition.setProperties(propertyMapList);
				beanDefinitionList.add(beanDefinition);
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return beanDefinitionList;
	}

  这段代码只是读取xml配置文件,并把相应的配置属性放到beanDefinition中,这里我们都把name,value写死在代码里面,这没关系,因为我们只是一个例子,但当你真正想做一个框架级别的东西,绝对不要这样。

我们的配置文件类似下面的:

<beans>
	<bean id="person" class="com.shun.test.Person" >
		<property name="name" value="shun"/>
		<property name="age" value="25" />
	</bean>
	
	<bean id="person2" class="com.shun.test.Person" >
		<property name="name" value="shun2" />
		<property name="age" value="24" />
	</bean>
</beans>

  我们就不搞那些什么schema和namespace了。

 

上面的代码应该不难理解,只是读取文件,然后解析bean标签,再接着解析property标签,保存name和value。

 

下面我们再来看看如何取得我们定义的bean,方法如下:

	@SuppressWarnings({  "rawtypes", "unchecked" })
	public Object getBean(String beanName){
		Object obj = null;
		//循环取得定义的bean TODO 这里考虑用map来实现会好点
		for (BeanDefinition beanDefinition:beanDefinitionList) {
			if (beanName.equals(beanDefinition.getName())) {
				String typeName = beanDefinition.getType();
				try {
					//通过指定的class类型生成对象
					Class bean = Class.forName(typeName);
					obj = bean.newInstance();
					List<Map<String,Object>> propertyMapList = beanDefinition.getProperties();
					//这里通过反射调用相应属性的set方法
					for (Map<String,Object> propertyMap:propertyMapList) {
						Method[] methods = bean.getMethods();
						for (Method method:methods){
							//这里取得所有方法,判断相应的setXXX方法,然后取得参数类型再执行
							if (("set"+DataUtils.capitalize(propertyMap.get("name").toString())).equals(method.getName())) {
								//取得set方法参数类型
								Class[] parameterTypes = method.getParameterTypes();
								if (parameterTypes[0].isAssignableFrom(String.class)) {
									method.invoke(obj, propertyMap.get("value"));
								} else {
									method.invoke(obj, Integer.valueOf(propertyMap.get("value").toString()));
								}
							}
						}
					}
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
		return obj;
	}

  这段代码主要是用了反射,通过配置的class属性,生成相应的类的实例,然后再根据property配置的name调用相应的set方法,然后再返回对象。因为没涉及到对象依赖,所以代码不复杂。

当然,当我们真正实现的时候,需要判断boolean,float,double等等类型,还有集合List,Array,Map,Properties等。这些都是我们现在没考虑的。

 

这几个方法都在我们的MyIocProcessor类中,它的代码如下:

package com.shun.bean;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.shun.utils.DataUtils;

public class MyIocProcessor {

	private List<BeanDefinition> beanDefinitionList;
	
	public MyIocProcessor(){}
	
	public MyIocProcessor(String configPath){
		this.beanDefinitionList = processConfig(this.getClass().getResource("/").getPath()+configPath);
	}
	
	//上面的两个方法,这里省略了
}

  这里处理的只是classpath里面的文件,根据传入的文件名,读取相应的xml文件,把读取的相应的bean信息放入到beanDefinitionList中,我们getBean的时候就根据它来取得。

 

我们例子中用到的bean只是一个简单的JAVABEAN:

package com.shun.test;

public class Person {

	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	public String toString(){
		return "Name:"+name+",Age:"+age;
	}
	
}

 

3)基本实现后,我们就来测试一下:

package com.shun.test;

import org.junit.Test;

import com.shun.bean.MyIocProcessor;


public class TestIoc {

	@Test
	public void test(){
		MyIocProcessor myIoc = new MyIocProcessor("beans.xml");
		Person person = (Person)myIoc.getBean("person2");
		System.out.println(person);
	}
	
}

  运行后,我们可以看到:

  我们可以修改,发现它都可以根据我们修改的值,打印出来,这证明我们的最基本的IOC是实现了,并且功能没问题。

 

依赖对象的注入,我们将会继续下来研究,如果大家有兴趣,可以自己研究。

 

最后再提醒大家,要注意身体,只有身体好,才谈得上赚钱。与广大javaeye上的朋友共勉。

  • 大小: 8.3 KB
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics