1.IOC容器的依赖注入

Spring中,依赖注入是在用户第一次向IOC容器索要Bean时触发的(通过getBean方法)。

在BeanFactory中我们看到getBean(String…)函数,它的具体实现在AbstractBeanFactory中:

  1. public Object getBean(String name) throws BeansException {
  2. return doGetBean(name, null, null, false);
  3. }

可以看到具体的注入过程转移到doGetBean(String…)中,在这个方法中,它首先从缓存中取,如果单件模式的bean已经被创建,则这种bean请求不需要重复的创建,调用

  1. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

跟踪进入getObjectForBeanInstance(…,null),可以知道因为最后的RootBeanDefinition参数是null,所以执行的是:

  1. if (mbd == null) {
  2. object = getCachedObjectForFactoryBean(beanName);
  3. }

而getCachedObjectForFactoryBean(beanName)中实现,其实现很简单,就是在缓存的bean map中查找bean返回。

继续回到doGetBean(String…)方法中:

  1. //取当前bean的所有依赖bean,这样就会触发getBean的递归调用,直至取到一个没有任何依赖的bean为止
  2. String[] dependsOn = mbd.getDependsOn();
  3. if (dependsOn != null) {
  4. for (String dependsOnBean : dependsOn) {
  5. getBean(dependsOnBean);
  6. //注册依赖的bean实例,具体实现过程在DefaultSingletonBeanRegistry中实现,其实就是将依赖的bean添加到依赖的hashmap中
  7. registerDependentBean(dependsOnBean, beanName);
  8. }
  9. }
  10. //通过调用createBean来,创建单例bean的实例
  11. if (mbd.isSingleton()) {
  12. sharedInstance = getSingleton(beanName, new ObjectFactory() {
  13. public Object getObject() throws BeansException {
  14. try {
  15. return createBean(beanName, mbd, args);
  16. }
  17. catch (BeansException ex) {
  18. destroySingleton(beanName);
  19. throw ex;
  20. }
  21. }
  22. });
  23. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  24. }
  25. //同样调用createBean创建prototype的bean实例
  26. else if (mbd.isPrototype()) {
  27. // It's a prototype -> create a new instance.
  28. Object prototypeInstance = null;
  29. try {
  30. beforePrototypeCreation(beanName);
  31. prototypeInstance = createBean(beanName, mbd, args);
  32. }
  33. finally {
  34. afterPrototypeCreation(beanName);
  35. }
  36. bean=getObjectForBeanInstance(prototypeInstance,name, beanName, mbd);
  37. }

继续看createBean(…),可以看到在AbstractBeanFactory中它只是个抽象类,具体的实现交给其子类(又见模板模式),进入子类AbstractAutowireCapableBeanFactory中看createBean的具体实现:

  1. Object beanInstance = doCreateBean(beanName, mbd, args);

其具体的实现转到doCreateBean(String…),这里我们看到与依赖注入关系比较密切的方法有createBeanInstance和populateBean。

  1. BeanWrapper instanceWrapper = null;
  2. if (mbd.isSingleton()) {
  3. instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  4. }
  5. if (instanceWrapper == null) {
  6. instanceWrapper = createBeanInstance(beanName, mbd, args);
  7. }
  1. // Initialize the bean instance.
  2. Object exposedObject = bean;
  3. try {
  4. populateBean(beanName, mbd, instanceWrapper);
  5. if (exposedObject != null) {
  6. exposedObject = initializeBean(beanName, exposedObject, mbd);
  7. }
  8. }

在createBeanInstance中生成了Bean所包含的Java对象,这个对象的生成有很多不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,生成的方式由相关联的BeanDefinition来指定,进入createBeanInstance方法,有:

  1. return instantiateUsingFactoryMethod(beanName, mbd, args);
  1. return instantiateBean(beanName, mbd);

上面是其中的两个实例化方法,上面的是在BeanDefinition的FactoryMethod存在的情况下,使用工厂方法对bean进行实例化。下面一个是使用默认的构造函数对bean进行实例化。我们进入instantiateBean(beanName,mbd),可以看到有:

  1. return getInstantiationStrategy().instantiate(mbd, beanName, parent);

因为getInstantiationStrategy()返回的默认的实例化策略,而默认的实例化策略是CglibSubclassingInstantiationStrategy,也即用cglib来对bean进行实例化。CGLIB是一个常用的字节码生成器的类库,它提供了一系列的API来提供Java的字节码生成和转换功能。

我们再次回到doCreateBean()中的populateBean方法,看看在实例化Bean对象生成的基础上,spring怎样把这些bean对象的依赖关系设置好,完成整个依赖注入过程。在populateBean中,先是取得在BeanDefinition中设置的property值,这些property来自对BeanDefinition的解析,接着便开始进行依赖注入过程:

  1. //开始进行依赖注入过程,先处理autowire的注入
  2. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  3. MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
  4. //根据bean的名字进行autowire过程
  5. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
  6. autowireByName(beanName, mbd, bw, newPvs);
  7. }
  8. //根据类型type进行autowire的过程
  9. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  10. autowireByType(beanName, mbd, bw, newPvs);
  11. }
  12. pvs = newPvs;
  13. }

最后在通过applyPropertyValues对属性进行注入:

  1. applyPropertyValues(beanName, mbd, bw, pvs);

接着我们到applyPropertyValues中去看具体的对属性进行解析然后注入的过程,在其中会调用BeanDefinitionValueResolver对BeanDefinition进行解析,

  1. BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

接着为解析值创建一个拷贝,拷贝的数据将会被注入到bean中,它会先对PropertyValue判断,如果其没有经过转换则会调用resolveValueIfNecessary进行解析,然后注入到property中。下面到BeanDefinitionValueResolver中去看一下解析过程的实现,在函数resolveValueIfNecessary中包含了所有对注入类型的处理,以RuntimeBeanReference(其是在对BeanDefinition进行解析时生成的数据对象)为例:

  1. if (value instanceof RuntimeBeanReference) {
  2. RuntimeBeanReference ref = (RuntimeBeanReference) value;
  3. return resolveReference(argName, ref);
  4. }
  5. //当注入类型为RuntimeBeanReference时,进入resolveReference(…):
  6. private Object resolveReference(Object argName, RuntimeBeanReference ref) {
  7. try {
  8. //从RuntimeBeanReference取得reference的名字
  9. String refName = ref.getBeanName();
  10. refName = String.valueOf(evaluate(refName));
  11. //如果ref是在双亲的IOC容器中,那就到双亲IOC容器中去取
  12. if (ref.isToParent()) {
  13. if (this.beanFactory.getParentBeanFactory() == null) {
  14. //抛出异常BeanCreationException
  15. ……
  16. }
  17. return this.beanFactory.getParentBeanFactory().
  18. getBean(refName);
  19. }
  20. else {
  21. //在当前IOC容器中去取bean
  22. Object bean = this.beanFactory.getBean(refName);
  23. this.beanFactory.registerDependentBean(refName, this.beanName);
  24. return bean;
  25. }
  26. }

在上面的实现中,无论是到双亲的IOC容器中去取,还是在当前IOC容器中取,都会触发一个getBean的过程,这就触发了相应的依赖注入的发生。

这就完成了resolve的过程,为依赖注入准备好了条件。但真正的把Bean对象设置到它所依赖的另一个Bean的属性中去的地方是在BeanWrapper的setPropertyValues中(在分析populateBean的时候有提到),其中处理的属性是各种各样的。setPropertyValues的具体实现是在BeanWrapper的子类BeanWrapperImpl中:

在doCreateBean中执行完populateBean,完成Bean的生成和依赖注入以后,开始对Bean进行初始化,这个初始化过程包含了对后置处理器的postProcessBeforeInitializtion回调,具体实现在initializeBean方法中:

  1. Object wrappedBean = bean;
  2. if (mbd == null || !mbd.isSynthetic()) {
  3. wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  4. }
  5. try {
  6. invokeInitMethods(beanName, wrappedBean, mbd);
  7. }
  8. catch (Throwable ex) {
  9. //抛出异常BeanCreationException
  10. }
  11. if (mbd == null || !mbd.isSynthetic()) {
  12. wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  13. }
  14. return wrappedBean;

spring IOC源码分析(3)的更多相关文章

  1. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  2. spring IoC源码分析 (3)Resource解析

    引自 spring IoC源码分析 (3)Resource解析 定义好了Resource之后,看到XmlFactoryBean的构造函数 public XmlBeanFactory(Resource  ...

  3. Spring IoC 源码分析 (基于注解) 之 包扫描

    在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫 ...

  4. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

  5. Spring Ioc源码分析系列--前言

    Spring Ioc源码分析系列--前言 为什么要写这个系列文章 首先这是我个人很久之前的一个计划,拖了很久没有实施,现在算是填坑了.其次,作为一个Java开发者,Spring是绕不开的课题.在Spr ...

  6. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  7. Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析

    Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...

  8. Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理

    Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理 前言 上一篇分析了BeanFactoryPostProcessor的作用,那么这一篇继续 ...

  9. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  10. Spring Ioc源码分析系列--Bean实例化过程(二)

    Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...

随机推荐

  1. DEM渲染洼地淹没图(转)

    http://blog.csdn.net/kikitamoon/article/details/18700555 首先要准备基础数据,一张DEM栅格图. 然后将掩膜水位以下的数据提取出来以备后用.这里 ...

  2. 绿荫工作室爱选修app内测

    下载地址:http://greendasungta.com/greencms/ixuanxiu.apk 开发目的是用于方便在校生的选课操作以及对选修课的交流.

  3. ruby 把字符串转为正则匹配表达式

    需求 函数,需要通过参数传递字符串,用来做正则匹配 reg = '[0-9]+' def func(str, reg) str.scan(reg) end 由于 reg 在其它地方定义, reg 是字 ...

  4. Xilinx 网站资源导读2

    Xilinx 网站资源导读 ———版权声明———–本文作者 Ricky Suwww.fpganotes.comrickysu.fpga@gmail.com 欢迎转载,转载请保持原样及署名商业使用须得到 ...

  5. [MSDN]使用 REST 处理文件夹和文件

    msdn: http://msdn.microsoft.com/zh-cn/library/dn292553.aspx 了解如何使用 SharePoint 2013 REST 界面对文件夹和文件执行基 ...

  6. 주기적으로 php파일 실행시키기 (PHP 파일 cron 으로 돌리기)

    크론탭에 추가 ]# crontab -e       한시간에 한번씩 원하는 페이지를 실행시키는 코드 0 * * * * wget -O - -q -t 1 http://domain.com ...

  7. 【高级JEE技术】JMX

    JMX即Java Manager Extentin(java 管理扩展)一种动态改变javabean属性值的技术,具体应用场景可以有很多.比如使用JMX作为线上应用的开关,在做一些新老系统改造的时候 ...

  8. 理解Spark的RDD

    RDD是个抽象类,定义了诸如map().reduce()等方法,但实际上继承RDD的派生类一般只要实现两个方法: def getPartitions: Array[Partition] def com ...

  9. 对象创建型模式------Abstract Factory(抽象工厂)

    1. 意图    提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类.2. 别名    Kit3. 动机        假设我们要开发一款游戏,当然为了吸引更多的人玩,游戏难度不能太大 ...

  10. PHP使用缓存生成静态页面

    http://www.cnblogs.com/lh460795/archive/2013/04/06/3003105.html 在apache / bin/ab.exe  可以做压力测试,该工具可以模 ...