zoukankan      html  css  js  c++  java
  • 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()+"	 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

  • 相关阅读:
    关于向量叉积求得法向量方向判断
    Winform菜单之ContextMenuStrip
    Winform菜单之Menustrip
    MDI窗体及涉及到的相关问题
    Winform主窗体的设置
    Winform登录、控制软件只运行一次、回车登录
    MessageBox详解
    Winform窗体
    Winform创建解决方案
    Winform开发入门集中培训系列文章
  • 原文地址:https://www.cnblogs.com/liangxinzhi/p/4275586.html
Copyright © 2011-2022 走看看