spring容器原理之浅析

来源:互联网 发布:unity3d真实地形制作 编辑:程序博客网 时间:2024/06/07 06:57

   本文只是使用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 {@Testpublic void test() {//初始化spring容器ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");//UnderClassPathXmlApplicationContext ac=new UnderClassPathXmlApplicationContext("applicationContext.xml");//从容其中获取beanIPersonDao 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) */@Overridepublic 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();}@Overridepublic 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和classprivate List<BeanSave> beansList;//用于存储实例化的bean和对应的idprivate 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的diPropertyListfor (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 blocke.printStackTrace();}}}//利用反射技术进行实例化对象private void instances() {// TODO Auto-generated method stubfor (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 blocke.printStackTrace();}}}//读取配置文件 获取id和class存储到beansList中private void readXml(String fileName) {// TODO Auto-generated method stubtry {//创建一个dom4j树的解析器SAXReader reader=new SAXReader();//利用类加载器获取到src下面的配置文件InputStream is=UnderClassPathXmlApplicationContext.class.getClassLoader().getResourceAsStream(fileName);//使用sax读取一个文档Document document=reader.read(is);//获取到根元素--即beansElement 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 blocke.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();}@Overridepublic 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();}@Overridepublic String toString() {return "DIProperty [name=" + name + ", value=" + value + ", ref=" + ref+ "]";}}


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

0 0
原创粉丝点击