【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)

文件夹
     【SSH进阶之路】一步步重构容器实现Spring框架——从一个简单的容器開始(八)
     【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主动查找和控制反转(九)
     【SSH进阶之路】一步步重构容器实现Spring框架——配置文件+反射实现IoC容器(十)
     【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)


      博文【SSH进阶之路】一步步重构容器实现Spring框架——从一个简单的容器開始(八),我们为了去掉接口对详细实现的依赖关系,封装了一个特别简陋的容器。
      博文【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主动查找和控制反转(九),我们利用控制反转,去掉了组件对容器的依赖。

      博文【SSH进阶之路】一步步重构容器实现Spring框架——配置文件+反射实现IoC容器(十),我们实现了读取配置文件,以及容器创建对象的灵活,简单的IoC。

  

      这篇博文的目标是不仅形似Spring,并且要神似Spring,进一步封装对象的依赖关系

      我们知道Spring框架,不仅能够依据配置创建对象,并且能够依据配置创建对象之间的依赖关系。对象之间的依

赖关系怎么配置呢,那我们先看一下配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans>

  <bean id="dao" class="com.tgb.container.dao.impl.Dao4MySqlImpl" />
  
  <bean id="service" class="com.tgb.container.service.impl.ServiceImpl">
  	<property name="dao" ref="dao"></property>
  </bean>
	
</beans>

      我们发现配置文件里多了两个属性:property和ref,表达了Service须要依赖Dao的关系,所以我们须要将dao注入

给Service,怎么做呢?我们仅仅须要像存储bean一样建立一个JavaBean就可以:

public class PropertyDefinition {

	private String name;
	private String ref;

	public PropertyDefinition(String name, String ref) {
		this.name = name;
		this.ref = ref;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRef() {
		return ref;
	}
	public void setRef(String ref) {
		this.ref = ref;
	}
	
}

      有了javabean,我们就仅仅须要专注于怎么为bean对象的属性注入值。我们能够利用内省来操作Bean类属性、事

件。一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描写叙述器

(PropertyDescriptor),通过这个属性描写叙述器就能够获取某个属性相应的getter/setter方法,然后我们就能够通过反

射机制来调用这些方法,最后将引用对象注入到属性中。

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

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

/**
 * 容器
 * 
 * @author liang
 * 
 */
public class ClassPathXmlApplicationContext implements BeanFactory {

	// 用于存放Bean
	private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
	// 用于存放Bean的实例
	private Map<String, Object> sigletons =new HashMap<String, Object>();
	
	
	public ClassPathXmlApplicationContext(String fileName) {

		this.readXML(fileName);
		
		this.instanceBeans();
		
		this.injectObject();
	}
	/**
	 * 为bean对象的属性注入值
	 */
	private void injectObject() {
		for (BeanDefinition beanDefinition :beanDefines) {
			Object bean = sigletons.get(beanDefinition.getId());
			if(bean != null){
				try {
					// 通过Introspector取得bean的定义信息,之后再取得属性的描写叙述信息,返回一个数组
					PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
					
					for(PropertyDefinition propertyDefinition:beanDefinition.getPropertys()){
						for(PropertyDescriptor properdesc: ps){
							if(propertyDefinition.getName().equals(properdesc.getName())){
								// 获取属性的setter方法,private
								Method setter = properdesc.getWriteMethod(); 
								if(setter != null){
									Object value = sigletons.get(propertyDefinition.getRef());
									// 同意訪问私有方法
									setter.setAccessible(true); 
									// 把引用对象注入到属性
									setter.invoke(bean, value); 
								}
								break;
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 完毕bean的实例化
	 */
	private void instanceBeans() {
		for(BeanDefinition beanDefinition : beanDefines){
			try {
				if(beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName().trim())){
					sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance() );
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 读取xml配置文件
	 */
	private void readXML(String fileName) {
		// 创建SAXBuilder对象
		SAXBuilder saxBuilder = new SAXBuilder();

		try {
			// 读取资源,获得document对象
			Document doc = saxBuilder.build(this.getClass().getClassLoader()
					.getResourceAsStream(fileName));
			// 获取根元素
			Element rootEle = doc.getRootElement();
			// 从根元素获得全部的子元素,建立元素集合
			List listBean = XPath.selectNodes(rootEle, "/beans/bean");

			// 遍历根元素的子元素集合,扫描配置文件里的bean
			for (int i = 0; i < listBean.size(); i++) {
				 // 将根元素beans下的bean子元素作为一个新的子根元素
				Element elementBean = (Element) listBean.get(i);
				//获取id属性值
				String id = elementBean.getAttributeValue("id");
				//获取class属性值
				String clazz = elementBean.getAttributeValue("class");
				
				BeanDefinition beanDefine = new BeanDefinition(id,clazz);
				// 获取子根元素bean下的全部property子元素
				List listProperty = elementBean.getChildren("property");
				// 遍历子根元素的子元素集合(即遍历property元素)
				for (int j = 0; j < listProperty.size(); j++) {
					// 获取property元素
					Element elmentProperty = (Element)listProperty.get(j);
					// 获取name属性值
					String propertyName = elmentProperty.getAttributeValue("name");
					// 获取ref属性值
					String propertyref = elmentProperty.getAttributeValue("ref");
					
					PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName,propertyref);
					
					beanDefine.getPropertys().add(propertyDefinition);
				}
				
				// 将javabean加入到集合中
				beanDefines.add(beanDefine);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


	/**
	 * 获取bean实例
	 */
	@Override
	public Object getBean(String beanName) {
		return this.sigletons.get(beanName);
	}
}


此时我们就能够把Service接口的set方法去掉了。
public interface Service {
	public void serviceMethod();
}


这里仅有部分代码,大家能够在以下链接中下载。

 

总结


        经过四篇博文的重构,我们实现了一个Spring的雏形,它能够让我们更加深刻的认识Spring的原理,对我们更加 

深入的学习Spring埋下了伏笔。


        源代码下载


posted @ 2015-05-10 11:07  hrhguanli  阅读(214)  评论(0编辑  收藏  举报