Spring 自动装配之依赖注入

依赖注入发生的时间

当 Spring IOC 容器完成了 Bean 定义资源的定位、载入和解析注册以后,IOC 容器中已经管理类 Bean
定义的相关数据,但是此时 IOC 容器还没有对所管理的 Bean 进行依赖注入,依赖注入在以下两种情况发生:
 
1)、用户第一次调用 getBean()方法时,IOC 容器触发依赖注入。
2)、当用户在配置文件中将<bean>元素配置了 lazy-init=false 属性,即让容器在解析注册 Bean 定义时进行预实例化,触发依赖注入。 
 
BeanFactory 接口定义了 Spring IOC 容器的基本功能规范,是 Spring IOC 容器所应遵守的最底层和
最基本的编程规范。BeanFactory 接口中定义了几个 getBean()方法,就是用户向 IOC 容器索取管理的
Bean 的方法,我们通过分析其子类的具体实现,理解 Spring IOC 容器在用户索取 Bean 时如何完成依
赖注入。 

在 BeanFactory 中我们可以看到 getBean(String...)方法,但它具体实现在 AbstractBeanFactory 中。
 

IOC 容器中那些鲜为人知的细节

通过前面章节中对 Spring IOC 容器的源码分析,我们已经基本上了解了 Spring IOC 容器对 Bean 定义
资源的定位、载入和注册过程,同时也清楚了当用户通过 getBean()方法向 IOC 容器获取被管理的 Bean
时,IOC 容器对 Bean 进行的初始化和依赖注入过程,这些是 Spring IOC 容器的基本功能特性。
Spring IOC 容器还有一些高级特性,如使用 lazy-init 属性对 Bean 预初始化、FactoryBean 产生或者
修饰 Bean 对象的生成、
IOC 容器初始化 Bean 过程中使用 BeanPostProcessor 后置处理器对 Bean 声
明周期事件管理等。
 
关于延时加载
通过前面我们对 IOC 容器的实现和工作原理分析,我们已经知道 IOC 容器的初始化过程就是对 Bean
定义资源的定位、载入和注册,此时容器对 Bean 的依赖注入并没有发生,依赖注入主要是在应用程序
第一次向容器索取 Bean 时,通过 getBean()方法的调用完成。
当 Bean 定义资源的<Bean>元素中配置了 lazy-init=false 属性时,容器将会在初始化的时候对所配置
的 Bean 进行预实例化,Bean 的依赖注入在容器初始化的时候就已经完成。这样,当应用程序第一次
向容器索取被管理的 Bean 时,就不用再初始化和对 Bean 进行依赖注入了,直接从容器中获取已经完
成依赖注入的现成 Bean,可以提高应用第一次向容器获取 Bean 的性能。
1、refresh()方法
先从 IOC 容器的初始化过程开始,我们知道 IOC 容器读入已经定位的 Bean 定义资源是从 refresh()方
法开始的,我们首先从 AbstractApplicationContext 类的 refresh()方法入手分析,源码如下:
在 refresh()方法中 ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();启
动了 Bean 定义资源的载入、注册过程,而 finishBeanFactoryInitialization 方法是对注册后的 Bean
定义中的预实例化(lazy-init=false,Spring 默认就是预实例化,即为 true)的 Bean 进行处理的地方。
 
2、finishBeanFactoryInitialization 处理预实例化 Bean
当 Bean 定义资源被载入 IOC 容器之后,容器将 Bean 定义资源解析为容器内部的数据结构
BeanDefinition 注册到容器中,AbstractApplicationContext 类中的 finishBeanFactoryInitialization()
方法对配置了预实例化属性的 Bean 进行预初始化过程,源码如下:
 
 
ConfigurableListableBeanFactory 是 一 个 接 口 , 其 preInstantiateSingletons() 方 法 由 其 子 类
DefaultListableBeanFactory 提供。
3、DefaultListableBeanFactory 对配置 lazy-init 属性单态 Bean 的预实例化
通过对 lazy-init 处理源码的分析,我们可以看出,如果设置了 lazy-init 属性,则容器在完成 Bean 定
义的注册之后,会通过 getBean 方法,触发对指定 Bean 的初始化和依赖注入过程,这样当应用第一次
 
向容器索取所需的 Bean 时,容器不再需要对 Bean 进行初始化和依赖注入,直接从已经完成实例化和
依赖注入的 Bean 中取一个现成的 Bean,这样就提高了第一次获取 Bean 的性能。
关于 FactoryBean 和 BeanFactory
在 Spring 中,有两个很容易混淆的类:BeanFactory 和 FactoryBean。
BeanFactory:Bean 工厂,是一个工厂(Factory),我们 Spring IOC 容器的最顶层接口就是这个
BeanFactory,它的作用是管理 Bean,即实例化、定位、配置应用程序中的对象及建立这些对象间的
依赖。
FactoryBean:工厂 Bean,是一个 Bean,作用是产生其他 bean 实例。通常情况下,这种 Bean 没有
什么特别的要求,仅需要提供一个工厂方法,该方法用来返回其他 Bean 实例。通常情况下,Bean 无
须自己实现工厂模式,Spring 容器担任工厂角色;但少数情况下,容器中的 Bean 本身就是工厂,其作
用是产生其它 Bean 实例。
当用户使用容器本身时,可以使用转义字符”&”来得到 FactoryBean 本身,以区别通过 FactoryBean
产生的实例对象和 FactoryBean 对象本身。在 BeanFactory 中通过如下代码定义了该转义字符:
String FACTORY_BEAN_PREFIX = "&";
如果 myJndiObject 是一个 FactoryBean,则使用&myJndiObject 得到的是 myJndiObject 对象,而
不是 myJndiObject 产生出来的对象。
1、FactoryBean 源码: 
2、AbstractBeanFactory 的 getBean()方法调用 FactoryBean:
在前面我们分析 Spring IOC 容器实例化 Bean 并进行依赖注入过程的源码时,提到在 getBean()方法
触发容器实例化 Bean 的时候会调用 AbstractBeanFactory 的 doGetBean()方法来进行实例化的过程,
源码如下:
在 上 面 获 取 给 定 Bean 的 实 例 对 象 的 getObjectForBeanInstance() 方 法 中 , 会 调 用
FactoryBeanRegistrySupport 类的 getObjectFromFactoryBean()方法,该方法实现了 Bean 工厂生
产 Bean 实例对象。
Dereference(解引用):一个在 C/C++中应用比较多的术语,在 C++中,”*”是解引用符号,而”&”
是引用符号,解引用是指变量指向的是所引用对象的本身数据,而不是引用对象的内存地址。
3、AbstractBeanFactory 生产 Bean 实例对象
AbstractBeanFactory 类中生产 Bean 实例对象的主要源码如下: 
从上面的源码分析中,我们可以看出,BeanFactory 接口调用其实现类的 getObject 方法来实现创建
Bean 实例对象的功能。
4、工厂 Bean 的实现类 getObject 方法创建 Bean 实例对象
FactoryBean 的实现类有非常多,比如:Proxy、RMI、JNDI、ServletContextFactoryBean 等等,
FactoryBean 接口为 Spring 容器提供了一个很好的封装机制,具体的 getObject()有不同的实现类根
据不同的实现策略来具体提供,我们分析一个最简单的 AnnotationTestFactoryBean 的实现源码:
 
其他的 Proxy,RMI,JNDI 等等,都是根据相应的策略提供 getObject()的实现。这里不做一一分析,
这已经不是 Spring 的核心功能,感兴趣的小伙可以再去深入研究。
再述 autowiring
Spring IOC 容器提供了两种管理 Bean 依赖关系的方式:
1)、显式管理:通过 BeanDefinition 的属性值和构造方法实现 Bean 依赖关系管理。
2)、autowiring:Spring IOC 容器的依赖自动装配功能,不需要对 Bean 属性的依赖关系做显式的声明,
只需要在配置好 autowiring 属性,IOC 容器会自动使用反射查找属性的类型和名称,然后基于属性的
类型或者名称来自动匹配容器中管理的 Bean,从而自动地完成依赖注入。
通过对 autowiring 自动装配特性的理解,我们知道容器对 Bean 的自动装配发生在容器对 Bean 依赖注
入的过程中。在前面对 Spring IOC 容器的依赖注入过程源码分析中,我们已经知道了容器对 Bean 实
例对象的属性注入的处理发生在 AbstractAutoWireCapableBeanFactory 类中的 populateBean()方
法中,我们通过程序流程分析 autowiring 的实现原理:
1、AbstractAutoWireCapableBeanFactory 对 Bean 实例进行属性依赖注入
应用第一次通过 getBean()方法(配置了 lazy-init 预实例化属性的除外)向 IOC 容器索取 Bean 时,容器
创 建 Bean 实 例 对 象 , 并 且 对 Bean 实 例 对 象 进 行 属 性 依 赖 注 入 ,
AbstractAutoWireCapableBeanFactory 的 populateBean()方法就是实现 Bean 属性依赖注入的功
能,其主要源码如下:
2、Spring IOC 容器根据 Bean 名称或者类型进行 autowiring 自动依赖注入
通过上面的源码分析,我们可以看出来通过属性名进行自动依赖注入的相对比通过属性类型进行自动依
赖 注 入 要 稍 微 简 单 一 些 , 但 是 真 正 实 现 属 性 注 入 的 是 DefaultSingletonBeanRegistry 类 的
registerDependentBean()方法。
3、DefaultSingletonBeanRegistry 的 registerDependentBean()方法对属性注入
通过对 autowiring 的源码分析,我们可以看出,autowiring 的实现过程:
a、对 Bean 的属性代调用 getBean()方法,完成依赖 Bean 的初始化和依赖注入。
b、将依赖 Bean 的属性引用设置到被依赖的 Bean 属性上。
c、将依赖 Bean 的名称和被依赖 Bean 的名称存储在 IOC 容器的集合中。
Spring IOC 容器的 autowiring 属性自动依赖注入是一个很方便的特性,可以简化开发时的配置,但是
凡是都有两面性,自动属性依赖注入也有不足,首先,Bean 的依赖关系在 配置文件中无法很清楚地看
出来,对于维护造成一定困难。其次,由于自动依赖注入是 Spring 容器自动执行的,容器是不会智能
判断的,如果配置不当,将会带来无法预料的后果,所以自动依赖注入特性在使用时还是综合考虑
 
 

Spring(四) SpringDI(1)的更多相关文章

  1. spring四种依赖注入方式(转)

    spring四种依赖注入方式!! 平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提 ...

  2. spring四种依赖注入方式

    一.Set注入 这是最简单的注入方式,假设有一个SpringAction,类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,然后创建SpringD ...

  3. Spring(四)注解配置Ioc

    原文链接:http://www.orlion.ga/216/ 一.@Autowired beans.xml配置成如下: <?xml version="1.0" encodin ...

  4. Spring(四)Bean注入方试

    一.构造方法注入 定义:通过构造函数来完成依赖关系的设定 优缺点: 在构造对象的同时,完成依赖关系的建立 如果关联的对象很多,那和不得不在构造方法上加入过多的参数 基中有index:如果指定索引从0开 ...

  5. spring(四) 手动整合web项目(SSH)

    清楚了spring的IOC 和 AOP,最后一篇就来整合SSH框架把,记录下来,以后应该会用的到. --WH 一.web项目中如何使用spring? 当tomcat启动时,就应该加载spring的配置 ...

  6. spring 四种数据源配置方式

    1.spring自带的数据源 DriverManagerDataSource XML代码: <bean id="dataSource" class="org.spr ...

  7. 【Spring四】AOP之XML配置

    AOP:Aspect Oriented  Programming 面向切面编程 面向切面编程的核心是动态代理设计模式.请先參见动态代理设计模式笔记. 以Hibernate保存一个对象到数据库为例,因为 ...

  8. 浅谈Spring(四)

    一.Spring+MyBatis整合 spring大大简化了Mybatis的开发步骤. 1.MyBatis的开发要点: mybatis-config.xml配置文件:配置与数据库的链接.mapper文 ...

  9. spring 四种依赖注入方式以及注解注入方式

    平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程 ...

随机推荐

  1. new String("ab")到底创建了几个对象说明

    new String("ab")到底创建了几个对象? 之前一直被这个问题困扰,网上一些描述的都不是很清楚,自己看了一些资料可算搞清楚了,那就在博客上记录一下吧! String st ...

  2. 我在华为OD的275天

    目录 0 - 时间线 1 - 为什么会去华为 OD 2 - 华为 OD 的工作内容 3 - OD 与华为自有员工的对比 4 - 那,到底要不要去华为 OD? 5 - 网传的 OD 转华为正编,真的假的 ...

  3. FPGA仿真的概念及语法特点

    以下是特权同学<FPGA设计+实战演练>书中的描述:      一个正规的设计需要花费在验证上的工作量,往往可能会占到整个开发流程的70%左右.验证通常分为仿真验证和板机验证.      ...

  4. kafka项目经验之如何进行Kafka压力测试、如何计算Kafka分区数、如何确定Kaftka集群机器数量

    @ 目录 Kafka压测 Kafka Producer(生产)压力测试 Kafka Consumer(消费)压力测试 计算Kafka分区数 Kafka机器数量计算 Kafka压测 用Kafka官方自带 ...

  5. SQL Server 邮箱告警配置

    目录 配置数据库邮件 * 手动启用数据库邮件功能 * 配置数据库邮件 * 测试数据库邮件 实现 JOB 任务运行状态的检测 * 定义操作员 * 新建死锁警报 * 设置 SQL Server 代理 创建 ...

  6. MySQL主从配置This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD FOR CHANNEL '' first.

    MySQL主从配置This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD ...

  7. 从零开始学Java (二)Hello

    1.新建Hello.java文件,写入以下内容 1 public class Hello { 2 public static void main(String[] args) { 3 System.o ...

  8. 题解 UVA11694 【Gokigen Naname谜题 Gokigen Naname】

    题目 题解 考场上连暴力都不会打的码农题,深搜是真的难 /kk 前置问题 怎么输出"\" cout<<"\\"; 2.怎么处理不在一个环里,可以考虑 ...

  9. LOJ10078

    CQOI 2005 重庆城里有 n 个车站,m 条双向公路连接其中的某些车站.每两个车站最多用一条公路连接,从任何一个车站出发都可以经过一条或者多条公路到达其他车站,但不同的路径需要花费的时间可能不同 ...

  10. 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)

    传送门 B-小宝的幸运数组 题目描述 对于小宝来说,如果一个数组的总和能够整除他的幸运数字k,就是他的幸运数组,而其他数组小宝都很讨厌.现在有一个长度为n的数组,小宝想知道这个数组的子数组中,最长的幸 ...