spring依赖注入的实现原理

来源:互联网 发布:电脑包 知乎 编辑:程序博客网 时间:2024/06/05 15:27

转载出处:http://matchless1688.iteye.com/blog/1038302


Spring 从核心而言,是一个DI 容器,其设计哲学是提供一种无侵入式的高扩展性框架。即无需代 

码中涉及Spring专有类,即可将其纳入Spring容器进行管理。 
作为对比,EJB则是一种高度侵入性的框架规范,它制定了众多的接口和编码规范,要求实现者必须 
遵从。侵入性的后果就是,一旦系统基于侵入性框架设计开发,那么之后任何脱离这个框架的企图都将付 
出极大的代价。 
为了避免这种情况,实现无侵入性的目标。Spring 大量引入了Java 的Reflection机制,通过动态 
调用的方式避免硬编码方式的约束,并在此基础上建立了其核心组件BeanFactory,以此作为其依赖注入 
机制的实现基础。 
org.springframework.beans包中包括了这些核心组件的实现类,核心中的核心为BeanWrapper 
和BeanFactory类。这两个类从技术角度而言并不复杂,但对于Spring 框架而言,却是关键所在。 

Bean Wrapper 
从Quick Start的例子中可以看到,所谓依赖注入,即在运行期由容器将依赖关系注入到组件之中。 
讲的通俗点,就是在运行期,由Spring根据配置文件,将其他对象的引用通过组件的提供的setter方法进 
行设定。 
我们知道,如果动态设置一个对象属性,可以借助Java的Reflection机制完成: 
Java代码  收藏代码
  1. Class cls = Class.forName("net.xiaxin.beans.User");  
  2. Method mtd = cls.getMethod("setName",new Class[]{String.class});  
  3. Object obj = (Object)cls.newInstance();  
  4. mtd.invoke(obj,new Object[]{"Erica"});  
  5. return obj;  

上面我们通过动态加载了User类,并通过Reflection调用了User.setName方法设置其name属性。 
对于这里的例子而言,出于简洁,我们将类名和方法名都以常量的方式硬编码。假设这些常量都是通过配 
置文件读入,那我们就实现了一个最简单的BeanWrapper。这个BeanWrapper的功能很简单,提供一个 
设置JavaBean属性的通用方法(Apache BeanUtils 类库中提供了大量针对Bean的辅助工具,如果有兴 
趣可以下载一份源码加以研读)。 
Spring BeanWrapper基于同样的原理,提供了一个更加完善的实现。 
看看如何通过Spring BeanWrapper操作一个JavaBean: 
Java代码  收藏代码
  1. Object obj = Class.forName("net.xiaxin.beans.User").newInstance();  
  2. BeanWrapper bw = new BeanWrapperImpl(obj);  
  3. bw.setPropertyValue("name""Erica");  
  4. System.out.println("User name=>"+bw.getPropertyValue("name"));  

对比之前的代码,相信大家已经知道BeanWrapper的实现原理。 
诚然,通过这样的方式设定Java Bean属性实在繁琐,但它却提供了一个通用的属性设定机制,而这 
样的机制,也正是Spring依赖注入机制所依赖的基础。 
通过BeanWrapper,我们可以无需在编码时就指定JavaBean的实现类和属性值,通过在配置文件 
加以设定,就可以在运行期动态创建对象并设定其属性(依赖关系)。 
上面的代码中,我们仅仅指定了需要设置的属性名“name”,运行期,BeanWrapper将根据Java 
Bean规范,动态调用对象的“setName”方法进行属性设定。属性名可包含层次,如对于属性名“address.zipcode”,BeanWrapper会调用“getAddress().setZipcode”方法。 

Bean Factory 
Bean Factory,顾名思义,负责创建并维护Bean实例。 
Bean Factory负责根据配置文件创建Bean实例,可以配置的项目有: 
1. Bean属性值及依赖关系(对其他Bean的引用) 
2. Bean创建模式(是否Singleton模式,即是否只针对指定类维持全局唯一的实例) 
3. Bean初始化和销毁方法 
4. Bean的依赖关系 
下面是一个较为完整的Bean配置示例: 
Java代码  收藏代码
  1. <beans>  
  2. <description>Spring Bean Configuration Sample</description>  
  3. <bean  
  4. id="TheAction" ⑴  
  5. class="net.xiaxin.spring.qs.UpperAction" ⑵  
  6. singleton="true" ⑶  
  7. init-method="init" ⑷  
  8. destroy-method="cleanup" ⑸  
  9. depends-on="ActionManager" ⑹  
  10. >  
  11. <property name="message">  
  12. <value>HeLLo</value> ⑺  
  13. </property>  
  14. <property name="desc">  
  15. <null/>  
  16. </property>  
  17. <property name="dataSource">  
  18. <ref local="dataSource"/> ⑻  
  19. </property>  
  20. </bean>  
  21. <bean id="dataSource"  
  22. class="org.springframework.jndi.JndiObjectFactoryBean">  
  23. <property name="jndiName">  
  24. <value>java:comp/env/jdbc/sample</value>  
  25. </property>  
  26. </bean>  
  27. </beans>  

下面的代码演示了如何通过BeanFactory获取Bean实例: 
InputStream is = new FileInputStream("bean.xml"); 
XmlBeanFactory factory = new XmlBeanFactory(is); 
Action action = (Action) factory.getBean("TheAction"); 
此时我们获得的Action实例,由BeanFactory进行加载,并根据配置文件进行了初始化和属性设定。 
联合上面关于BeanWrapper的内容,我们可以看到,BeanWrapper实现了针对单个Bean的属性设 
定操作。而BeanFactory则是针对多个Bean的管理容器,根据给定的配置文件,BeanFactory从中读取 
类名、属性名/值,然后通过Reflection机制进行Bean加载和属性设定。
原创粉丝点击