7.编码剖析Spring依赖注入的原理

前面给大家介绍过,使用Spring,可以使用里面的控制反转把依赖对象交给 Spring 管理,并把依赖对象通过容器注入到组件内部。那么在Spring里面,该如何把对象注入到组件内部呢?
创建一个PersonDao对象,并把这个对象注入到PersonServiceBean中
 
package cn.itcast.dao.impl; 
 
import cn.itcast.dao.PersonDao; 
public class PersonDaoBean implements PersonDao { 
    public void add(){ 
        System.out.println("执行PersonDaoBean里的add()方法"); 
    } 
}

面向接口编程,所以要把接口抽取出来。
package cn.itcast.dao; 
 
public interface PersonDao { 
    public void add(); 
}
接口跟实现类不要放一块,接下来,如何将PersonDaoBean对象注入进PersonServiceBean,注入方式有两种:一种是构造器参数,另一种是通过属性的set方法注入。 下面介绍通过属性的set方法我们该如何注入PersonDaoBean对象

PersonServiceBean.java
package cn.itcast.service.impl; 
 
import cn.itcast.dao.PersonDao; 
import cn.itcast.service.PersonService; 
 
public class PersonServiceBean implements PersonService { 
    private PersonDao personDao; 
     
    public PersonDao getPersonDao() { 
        return personDao; 
    } 
 
    public void setPersonDao(PersonDao personDao) { 
        this.personDao = personDao; 
    } 
     
    public void save(){ 
        personDao.add(); 
    } 
}
大家可以看到,在服务层 iFiona绝色CC霜 的这个类里面,我们并没有看到PersonDaoBean的身影,也就是说我们并不关心这个实现类是谁,我们通过PersonDao这个接口去引用注入进来的对象,在通过接口调用它的方法。这样的话,服务层的组件和DAO层的组件已经进行彻底的解耦了。
看下在beans.xml里如何为personDao这个属性注入PersonDaoBean这个bean呢? 首先要把personDao这个bean配置在Spring中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
           <bean id="personDao" class="cn.itcast.dao.impl.PersonDaoBean"></bean>
          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
          	<property name="personDao" ref="personDao"></property>
          </bean>
</beans>

property这个元素就是用于为属性注入值,name填写的是属性的名称
ref填写的值就是我们要注入的bean的名称。Spring会根据这个名称从Spring容器里面得到这个bean,因为这个bean默认在Spring容器实例化后就会被实例化,所以它在容器里面根据ref里的名称得到相应的bean,然后把这个bean通过反射技术就付给了里面的属性。这就是Spring执行的过程。
我们看下我们注入的personDao这个bean是否能够成功注入呢?判断是否能够成功注入很简单,在PersonServiceBean.java里的save方法,调用了personDao.add()方法,如果注入不成功的话,就会出现空指针异常;如果能输出add方法里面打印的那句话,就代表注入是成功的

package junit.test; 
 
import org.junit.BeforeClass; 
import org.junit.Test; 
import org.springframework.context.support.AbstractApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
 
import cn.itcast.service.PersonService; 
 
public class SpringTest { 
 
    @BeforeClass 
    public static void setUpBeforeClass() throws Exception { 
    } 
 
    @Test public void instanceSpring(){ 
    AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
        PersonService personService = (PersonService)ctx.getBean("personService"); 
    personService.save(); 
        ctx.close(); 
    } 
}

运行单元测试代码,控制台输出“执行PersonDaoBean里的add()方法”。说明注入成功了

这时候,大家思考下控制反转这个概念,原先我们对象的创建是由应用本身创建的。现在对象的创建是由容器帮我们创建,并且由容器注入进来,这时候控制权发生了转移,这就是所谓的控制反转。大家印象应该比较深刻了吧?


注入就介绍到这里,有同学可能会问:那么它内部到底是如何实现的呢? 接下来就在原先的传智播客版Spring容器中实现这个过程,解剖一下Spring的内部细节。
首先要建一个java bean,用来存储property的信息,然后property的信息再通过一个集合存在bean里面

PropertyDefinition.java
package junit.test; 
 
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; 
    } 
     
}

BeanDefinition.java
package junit.test; 
 
import java.util.ArrayList; 
import java.util.List; 
 
public class BeanDefinition { 
    private String id; 
    private String className; 
    private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>(); 
    //通过一个集合,来存放property的信息 
 
    public BeanDefinition(String id, String className) { 
        this.id = id; 
        this.className = className; 
    } 
    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 List<PropertyDefinition> getPropertys() { 
        return propertys; 
    } 
    public void setPropertys(List<PropertyDefinition> propertys) { 
        this.propertys = propertys; 
    } 
 
}

ItcastClassPathXMLApplicationContext.java
package junit.test; 
 
import java.beans.IntrospectionException; 
import java.beans.Introspector; 
import java.beans.PropertyDescriptor; 
import java.lang.reflect.Method; 
import java.net.URL; 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.HashSet; 
import java.util.List; 
import java.util.Map; 
 
import org.dom4j.Document; 
import org.dom4j.Element; 
import org.dom4j.XPath; 
import org.dom4j.io.SAXReader; 
 
/** 
 * 传智播客版的Spring容器 
 */
 
public class ItcastClassPathXMLApplicationContext { 
    private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>(); 
    private Map<String, Object> sigletons = new HashMap<String, Object>(); 
 
    // 存放bean实例 
 
    public ItcastClassPathXMLApplicationContext(String filename) { 
        // 模拟内部的实现,首先要读取配置文件,可以用dom4j 
        this.readXML(filename); 
        // 读取完bean之后,Spring要对bean进行实例化,怎么实现实例化呢? 通过反射机制就很容易做到 
        this.instanceBeans(); 
        this.injectObject(); 
    } 
 
    /** 
    * 实现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) { 
                // 通过反射技术把bean都创建出来 
                e.printStackTrace(); 
            } 
        } 
 
    } 
 
    /** 
     * 为bean对象的属性注入值 
     */
 
    private void injectObject() { 
        for (BeanDefinition beanDefinition : beanDefines) { 
            Object bean = sigletons.get(beanDefinition.getId()); 
            if (bean != null) { 
            try { 
                PropertyDescriptor[] ps = Introspector.getBeanInfo( 
                    bean.getClass()).getPropertyDescriptors(); 
                //Introspector通过这个类可以取得bean的定义信息 
                for (PropertyDefinition propertyDefinition : beanDefinition.getPropertys()) { 
                    for (PropertyDescriptor properdesc : ps) { 
                        if (propertyDefinition.getName().equals(properdesc.getName())) { 
                            Method setter = properdesc.getWriteMethod();// 获取属性的setter方法 
                                                                            // ,private 
                            if (setter != null) {//属性可能没有set方法,所以这里要判断一下 
                            Object value = sigletons.get(propertyDefinition.getRef()); 
                            setter.setAccessible(true);//如果set方法是私有的话,要设置它允许被访问 
                            setter.invoke(bean, value);// 把引用对象注入到属性 
                        } 
                        break
                    } 
                } 
            } 
                } catch (Exception e) { 
                } 
    } 
        } 
    } 
 
    /** 
    * 读取xml配置文件 
    */
 
    private void readXML(String filename) { 
        SAXReader saxReader = new SAXReader(); 
        Document document = null
        try { 
            URL xmlpath = this.getClass().getClassLoader().getResource(filename); 
            document = saxReader.read(xmlpath); 
            Map<String, String> nsMap = new HashMap<String, String>(); 
            nsMap.put("ns""http://www.springframework.org/schema/beans");// 加入命名空间 
            XPath xsub = document.createXPath("//ns:beans/ns:bean");// 创建beans/bean查询路径 
            xsub.setNamespaceURIs(nsMap);// 设置命名空间 
            List<Element> beans = xsub.selectNodes(document);// 获取文档下所有bean节点 
            for (Element element : beans) { 
                String id = element.attributeValue("id");// 获取id属性值 
                String clazz = element.attributeValue("class"); // 获取class属性值 
                BeanDefinition beanDefine = new BeanDefinition(id, clazz); 
                XPath propertysub = element.createXPath("ns:property"); 
                propertysub.setNamespaceURIs(nsMap);// 设置命名空间 
                List<Element> propertys = propertysub.selectNodes(element); 
                for (Element property : propertys) { 
                    String propertyName = property.attributeValue("name"); 
                    String propertyref = property.attributeValue("ref"); 
                    System.out.println(propertyName + " = " + propertyref); 
                    PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref); 
                    beanDefine.getPropertys().add(propertyDefinition); 
                } 
                beanDefines.add(beanDefine); 
            } 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
 
    /** 
    * 获取bean实例 
    */
 
    public Object getBean(String beanName) { 
        return this.sigletons.get(beanName); 
    } 
}


SpringTest.java
package junit.test; 
 
import org.junit.BeforeClass; 
import org.junit.Test; 
import cn.itcast.service.PersonService; 
 
public class SpringTest { 
 
    @BeforeClass 
    public static void setUpBeforeClass() throws Exception { 
    } 
 
    @Test public void instanceSpring(){ 
        ItcastClassPathXMLApplicationContext ctx = new ItcastClassPathXMLApplicationContext("beans.xml"); 
        PersonService personService = (PersonService)ctx.getBean("personService"); 
                //通过传智播客版的Spring容器得到这个bean 
        personService.save(); 
    } 
}
运行单元测试代码,如果是空指针异常,则注入不成功;如果注入成功,则打印add里面的语句。
结果控制台输出是:
personDao = personDao
执行PersonDaoBean里的add()方法

成功了,通过传智播客版的类也可以模拟Spring的注入功能,相信大家通过参看这段代码后,会对Spring如何注入依赖对象会很清楚了。这也是为什么传智播客的同学出去后,对原理理解的那么透彻了。。。



原文转载地址:::::http://www.java63.com/spring/ioc_principle.html




http://www.java63.com/catalog.asp?tags=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2Spring2%2E5%E8%A7%82%E7%9C%8B%E7%AC%94%E8%AE%B0&page=3

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值