博文【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);
- }
- }
- public interface Service {
- public void serviceMethod();
- }
这里仅有部分代码,大家可以在下面链接中下载。
总结
经过四篇博文的重构,我们实现了一个Spring的雏形,它可以让我们更加深刻的认识Spring的原理,对我们更加
深入的学习Spring埋下了伏笔。