spring容器原理之浅析

   本文只是使用dom4j与反射技术,模拟spring容器从配置文件读取配置信息,进而为用户提供实体bean,并且解析了使用了setter进行依赖注入的属性.


首先看看正版的spring所做的事情,如下

junit case test代码

SpringTest.java

package com.undergrowth.test;

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.undergrowth.bean.inter.IPersonDao;
import com.undergrowth.utils.UnderClassPathXmlApplicationContext;

public class SpringTest {

	@Test
	public void test() {
		//初始化spring容器
		ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
		//UnderClassPathXmlApplicationContext ac=new UnderClassPathXmlApplicationContext("applicationContext.xml");
		//从容其中获取bean
		IPersonDao iPersonDao=(IPersonDao) ac.getBean("personDao");
		//显示消息
		iPersonDao.sayWhat("我来自spring容器外");
	}

}

看到上面的代码 很简单 初始化spring容器 并从中取出id为personDao的实例 并且调用相应实体的sayWhat方法

控制台输出信息:

log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
你说的是:我来自spring容器外
person的名称为:李四	 person的年龄为:22

所以现在看看spring配置文件applicationContext.xml的代码

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
    
    <bean id="person" class="com.undergrowth.bean.Person" >
    <!-- 使用构造器参数进行注入对象
    	<constructor-arg index="0" type="java.lang.String" value="张三"></constructor-arg>
    	<constructor-arg index="1" value="20"></constructor-arg>
     -->	
     <!-- 使用属性的setter方法进行注入 -->
      <property name="name" value="李四"></property>
      <property name="age" value="22"></property>
    </bean>

	<bean id="personDao" class="com.undergrowth.bean.inter.imple.PersonDao">
	<!-- 使用属性的setter方法进行注入 -->
	  <property name="person" ref="person"></property>
	</bean>
</beans>
上面的配置文件代码 对person对象使用了setter进行依赖注入了 personDao也使用了setter进行依赖注入


现在来看PersonDao.java代码

package com.undergrowth.bean.inter.imple;

import com.undergrowth.bean.Person;
import com.undergrowth.bean.inter.IPersonDao;



public class PersonDao implements IPersonDao  {
	
	private Person person;
	
	
	public Person getPerson() {
		return person;
	}


	public void setPerson(Person person) {
		this.person = person;
	}


	/* (non-Javadoc)
	 * @see com.undergrowth.bean.inter.imple.IPersonDao#sayWhat(java.lang.String)
	 */
	@Override
	public void sayWhat(String msg)
	{
		System.out.println("你说的是:"+msg);
		System.out.println("person的名称为:"+person.getName()+"\t person的年龄为:"+person.getAge());
	}
}

Person.java代码

package com.undergrowth.bean;

public class Person {
	
	private String name;
	private Integer age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Person(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}
	public Person() {
		super();
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
}



上面即使正版spring实现的功能,现在使用模拟板的spring容器

UnderClassPathXmlApplicationContext.java

package com.undergrowth.utils;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.undergrowth.bean.BeanSave;
import com.undergrowth.bean.DIProperty;

/*
 * 该类的作用用来简单的模拟spring容器的工作流程
 * 只是模拟spring的简单流程 复杂的是不行的
 * spring容器的工作流程为
 * 1.解析spring配置文件--即applicationContext.xml,获取配置文件中的bean的id和class
 * 2.利用反射技术,生成class相对应的对象实例
 * 3.提供一个getBean方法给外界,让用户获取到配置文件中的相应的class的对象实例
 * 所以本类也是分为三步
 */
public class UnderClassPathXmlApplicationContext {
	
	//用于存储从配置文件中读取到的id和class
	private List<BeanSave> beansList;
	//用于存储实例化的bean和对应的id
	private Map<String, Object> beansInstanceMap;
	
	public UnderClassPathXmlApplicationContext(String fileName)
	{
		beansList=new ArrayList<BeanSave>();
		beansInstanceMap=new HashMap<String, Object>();
		this.readXml(fileName);
		this.instances();
		this.injectProperty();
	}

	//使用反射技术注入属性值
	private void injectProperty() {
		// TODO Auto-generated method stub
		//遍历所存储的bean信息 查找是否有diPropertyList属性需要注入
		for (Iterator iterator = beansList.iterator(); iterator.hasNext();) {
			BeanSave bean = (BeanSave) iterator.next();
			//通过bean的id获取到beansInstanceMap中的实例化的bean,然后获取bean的属性信息
			try {
				//获取bean的实例对象
				Object beanInstanceObject=beansInstanceMap.get(bean.getId());
				PropertyDescriptor[] propertyDescriptors=Introspector.getBeanInfo(beanInstanceObject.getClass()).getPropertyDescriptors();
				//迭代bean的属性信息
				for (int i = 0; i < propertyDescriptors.length; i++) {
					//迭代bean的diPropertyList
					for (Iterator iterator2=bean.getDiPropertyList().iterator();iterator2.hasNext();) {
						DIProperty diProperty=(DIProperty) iterator2.next();
						//比较bean的属性名称和bean的diPropertyList的name是否一致 如果一致的话 即给他注入值
						if(propertyDescriptors[i].getName().equals(diProperty.getName())){
							Method setterMethod=propertyDescriptors[i].getWriteMethod();
							Object value=null;
							//判断value字段是否为空 不为空的话 就赋值
							if(diProperty.getValue()!=null){
								value=ConvertUtils.convert(diProperty.getValue(), propertyDescriptors[i].getPropertyType());
							}
							//判断ref字段是否为空
							if(diProperty.getRef()!=null)
							{
								value=beansInstanceMap.get(diProperty.getRef());
							}
							//给对象注入值
							setterMethod.invoke(beanInstanceObject, value);
							break;
						}
					}
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}

	//利用反射技术进行实例化对象
	private void instances() {
		// TODO Auto-generated method stub
		for (BeanSave beanSave : beansList) {
			//判断bean的id属性是否存在
			try {
				if(beanSave.getId()!=null&&!"".equals(beanSave.getId())){
					//将实例化的对象存放到beansInstanceMap中
					beansInstanceMap.put(beanSave.getId(), Class.forName(beanSave.getClassName()).newInstance());
				}
			}  catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	//读取配置文件 获取id和class存储到beansList中
	private void readXml(String fileName) {
		// TODO Auto-generated method stub
		try {
			//创建一个dom4j树的解析器
			SAXReader reader=new SAXReader();
			//利用类加载器获取到src下面的配置文件
			InputStream is=UnderClassPathXmlApplicationContext.class.getClassLoader().getResourceAsStream(fileName);
			//使用sax读取一个文档
			Document document=reader.read(is);
			//获取到根元素--即beans
			Element rootElement=document.getRootElement();
			//遍历beans下面的bean元素
			for(Iterator iterator=rootElement.elementIterator("bean");iterator.hasNext();)
			{
				//获取到bean元素
				Element beanElement=(Element) iterator.next();
				BeanSave beanSave=new BeanSave();
				//获取到bean中id和class属性的值 并存放在beansList中 用于下步的反射处理
				beanSave.setId(beanElement.attributeValue("id"));
				beanSave.setClassName(beanElement.attributeValue("class"));
				List<DIProperty> diPropertyList=new ArrayList<DIProperty>();
				//接下来获取是否有使用setter进行注入的属性 有的话 存储起来
				for (Iterator iterator2=beanElement.elementIterator();iterator2.hasNext();) {
					Element propertyElement=(Element) iterator2.next();
					DIProperty diProperty=new DIProperty();
					diProperty.setName(propertyElement.attributeValue("name"));
					diProperty.setValue(propertyElement.attributeValue("value"));
					diProperty.setRef(propertyElement.attributeValue("ref"));
					//将注入的属性存储到diProperty中
					diPropertyList.add(diProperty);
				}
				beanSave.setDiPropertyList(diPropertyList);
				beansList.add(beanSave);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public Object getBean(String key)
	{
		return beansInstanceMap.get(key);
	}
	
}

上面即使模拟版的容器 代码里面都做了详细的注释 所以就不多说了

只是将SpringTest类里面的test测试方法中的ApplicationContext换成UnderClassPathXmlApplicationContext即可 会发现功能与正版spring容器的效果是一致的


BeanSave.java代码

package com.undergrowth.bean;

import java.util.List;

/*
 * 用于存储spring配置文件中bean的id和class属性
 * 这里还加入一个解析bean中使用setter方法进行注入的属性
 */
public class BeanSave {
	private String id;
	private String className;
	
	//用于保存使用setter进行注入的属性集合
	private List<DIProperty> diPropertyList;
	
	
	public List<DIProperty> getDiPropertyList() {
		return diPropertyList;
	}
	public void setDiPropertyList(List<DIProperty> diPropertyList) {
		this.diPropertyList = diPropertyList;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getClassName() {
		return className;
	}
	public void setClassName(String className) {
		this.className = className;
	}
	public BeanSave(String id, String className) {
		super();
		this.id = id;
		this.className = className;
	}
	public BeanSave(String id, String className, List<DIProperty> diPropertyList) {
		super();
		this.id = id;
		this.className = className;
		this.diPropertyList = diPropertyList;
	}
	public BeanSave() {
		super();
	}
	@Override
	public String toString() {
		return "BeanSave [id=" + id + ", className=" + className + "]";
	}
	
	
}


DIProperty.java代码

package com.undergrowth.bean;

/*
 * 用于保存使用<property name="" value=""></property>或者是
 * <property name="" ref=""></property>进行注入的属性
 */
public class DIProperty {
	private String name;
	private String value;
	private String ref;

	public String getName() {
		return name;
	}

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

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getRef() {
		return ref;
	}

	public void setRef(String ref) {
		this.ref = ref;
	}

	public DIProperty(String name, String value, String ref) {
		super();
		this.name = name;
		this.value = value;
		this.ref = ref;
	}

	public DIProperty() {
		super();
	}

	@Override
	public String toString() {
		return "DIProperty [name=" + name + ", value=" + value + ", ref=" + ref
				+ "]";
	}
	
}


上面即使对spring容器的原理的浅析 重点是搞懂自定义的容器 UnderClassPathXmlApplicationContext

你可能感兴趣的:(spring容器)