本文只是使用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
+ "]";
}
}