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