阅读Spring IoC部分源码有一段时间了,经过不断的单步调试和参阅资料,对Spring容器中bean管理有了一定的了解。这里从bean的几个状态的角度出发,研究下IoC容器。

一、原材料

  Xml中的bean定义配置(或者注解)、及Java代码

    <bean id="book" name="book" class="com.sky.vo.Book" scope="singleton" init-method="productBook" destroy-method="destroyBook">
        <property name="title" value="小王子"/>
    </bean>
public class Book implements InitializingBean, DisposableBean, BeanFactoryAware, ApplicationContextAware{
    private String title;

    public Book(){
        System.out.println("调用了Book默认构造函数");
    }

    public void productBook(){
        System.out.println("书本初始化init-method");
    }
  ///.......
}

二、半成品

  BeanDefinition是Spring中很重要的一个接口,作用是对bean的一种抽象(简单的理解是他的属性对应了<bean>中定义的属性和子节点等信息)。他是半成品,所以我们几乎不会直接使用,而是在IoC容器内部流通!

阅读源码发现是用SAX解析xml,然后保存到BeanDefinition对象。以下是关键的伪代码

/*
**BeanDefinitionParserDelegate.java
*/
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
    AbstractBeanDefinition bd = createBeanDefinition(className, parent){
      GenericBeanDefinition bd = new GenericBeanDefinition();
      bd.setBeanClassName(className);
      return bd;
  }

    parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    parseConstructorArgElements(ele, bd);
    parsePropertyElements(ele, bd);
  //省略了解析其他子节点的代码
}

核心的解析过程比较简单,创建了GenericBeanDefinition,然后节点属性和<property>、<construct-arg>等设置为他的属性。

那么,还有一个比较关键的步奏,就是转化后的BeanDefinition存放的时机和位置,是在Spring IoC容器启动时存放的,具体代码:

/*
**DefaultListableBeanFactory.java
*/
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
        synchronized (this.beanDefinitionMap) {
            Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            if (oldBeanDefinition != null) {
                if (!this.allowBeanDefinitionOverriding) {
                    throw new BeanDefinitionStoreException("Cannot register bean definition  for bean , There is already bound.");
                }
                else {
                    this.logger.info("Overriding bean definition for bean, replacing oldBeanDefinition");
                }
            }
            else {
                this.beanDefinitionNames.add(beanName);
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
}

可以看到最终存储到了beanDefinitionMap这个Map中,来看看他的定义(DefaultListableBeanFactory.java中)

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

这样,Spring容器就持有了BeanDefinition。

三、成品

获取成品就是获取具体的bean对象,其实就是Object bean = BeanFactroy.getBean(beanName).这里根据scope的不同,会有一些不同,简而言之:

A、singleton在IoC容器初始化时实例化,并缓存到Map中;getBean(singletonBeanName)时从这个Map中取即可;

  如果singleton同时lazy-init="true",第一次getBean时缓存到Map,以后从Map中取;

B、getBean(prototypeBeanName)时每次都实例化新的bean对象;

1、singleton的bean

  直接看伪代码(截取了部分关键代码)

    //从getBean入手
    BeanFactroy.getBean("beanName"){
        AbstractBeanFactory.createBean{
            Object beanInstance = doCreateBean(beanName, mbd, args){
                // Eagerly check singleton cache for manually registered singletons.
                //这是返回了缓存的singleton的bean:com.sky.vo.Book@1a1ecd
                Object sharedInstance = getSingleton(beanName){
                    return Object singletonObject = this.singletonObjects.get(beanName);
                }
                return (T)bean;
            }
        }
    }

从源码我们可以看到,对于singleton的bean,我们直接从singletonObjects这个Map中取!

public class DefaultSingletonBeanRegistry{
  /**缓存了singleton的bean:beanName--->bean实例*/
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
}

那么bean实例是什么时候存入到这个map的呢?看源码知道是在IoC容器启动时。

通过多次分析源码,我们知道AbstractApplicationContext.refresh()完成了容器的初始化过程,singleton的bean的实例化也是在这完成的。

    public void AbstractApplicationContext.refresh(){
        //创建Spring容器
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Instantiate all remaining (non-lazy-init) singletons.
        AbstractApplicationContext.finishBeanFactoryInitialization(beanFactory){
            DefaultListableBeanFactory.preInstantiateSingletons(){
                RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                    //然后,执行了getBean的操作!!!这里执行的代码就是BeanFactory.getBean(beanName)部分
                    AbstractBeanFactory.getBean(beanName){
                        AbstractBeanFactory.doGetBean(beanName){
                            //缓存为空
                            Object sharedInstance = getSingleton(beanName);
                            //如果是singleton
                            if (mbd.isSingleton()) {
                                DefaultSingletonBeanRegistry.getSingleton(){
                                    synchronized (this.singletonObjects) {
                                        //这里缓存也是空的
                                        Object singletonObject = this.singletonObjects.get(beanName);
                                        //实例化bean
                                        singletonObject = singletonFactory.getObject(){
                                            Object beanInstance = doCreateBean(beanName, mbd, args);
                                        }
                                        //这里非常重要,把实例化的bean放到map中
                                        addSingleton(beanName, singletonObject){
                                            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

截取了部分关键的代码片段,可以看到Spring容器初始化时会把实例化的singleton的bean放到singletonObject

Spring IoC源码解读——谈谈bean的几种状态的更多相关文章

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

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

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

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

  3. spring beans源码解读之--Bean的注解(annotation)

    随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...

  4. spring beans源码解读之--Bean的定义及包装

    bean的定义,包装是java bean的基础.再怎么强调它的重要性都不为过,因此深入 了解这块的代码对以后的代码研究可以起到事半功倍的功效. 1. Bean的定义BeanDefinition 1.1 ...

  5. spring beans源码解读之--bean definiton解析器

    spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean ...

  6. Spring Ioc源码分析系列--容器实例化Bean的四种方法

    Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...

  7. Spring Ioc源码分析系列--@Autowired注解的实现原理

    Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...

  8. Spring Ioc源码分析系列--自动注入循环依赖的处理

    Spring Ioc源码分析系列--自动注入循环依赖的处理 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到Spring创建bean出现循环依赖的时候并没有深入去分 ...

  9. 深入Spring IOC源码之ResourceLoader

    在<深入Spring IOC源码之Resource>中已经详细介绍了Spring中Resource的抽象,Resource接口有很多实现类,我们当然可以使用各自的构造函数创建符合需求的Re ...

随机推荐

  1. 锁&锁与指令原子操作的关系 & cas_Queue

    锁 锁以及信号量对大部分人来说都是非常熟悉的,特别是常用的mutex.锁有很多种,互斥锁,自旋锁,读写锁,顺序锁,等等,这里就只介绍常见到的, 互斥锁 这个是最常用的,win32:CreateMute ...

  2. ::after::before清除浮动原理

    先来看一段代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  3. 深入理解javascript作用域系列第四篇——块作用域

    × 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...

  4. 去除IE6浏览器下获得焦点的元素的虚线框的两个小办法

    [1]onfocus = "this.blur()"//得到焦点时,失去焦点 e.g. <a href="#" onfocus = "this. ...

  5. ASP.NET MVC 4中如何为不同的浏览器自适应布局和视图

    在ASP.NET MVC 4中,可以很简单地实现针对不同的浏览器自适应布局和视图.这个得归功于MVC中的"约定甚于配置"的设计理念. 默认的自适应 MVC 4自动地为移动设备浏览器 ...

  6. C#基础-----面向对象

    C#基础-----面向对象(一) 分类: C#基础2015-06-08 14:10 2人阅读 评论(0) 收藏 举报 1,面向对象 什么是面向对象?一个分析问题的方式(增强了程序的可扩展性) 几个名词 ...

  7. 机器学习&数据挖掘笔记_23(PGM练习七:CRF中参数的学习)

    前言: 本次实验主要任务是学习CRF模型的参数,实验例子和PGM练习3中的一样,用CRF模型来预测多张图片所组成的单词,我们知道在graph model的推理中,使用较多的是factor,而在grap ...

  8. 轻松自动化---selenium-webdriver(python) (七)

    本节知识点: 多层框架或窗口的定位: switch_to_frame() switch_to_window() 智能等待: implicitly_wait() 对于一个现代的web应用,经常会出现框架 ...

  9. Responsive Web CSS – 在线响应式布局创建器

    如果您已经使用了 CSS 或前端框架,创建响应式布局应该不难. 然而,如果你刚涉足这类布局,Responsive Web CSS 可以帮助你快速上手. 这是一个基于 Web 的工具,使任何人都可以通过 ...

  10. EasyUI笔记

    以下功能实现朋友们若有更好的思路办法,欢迎留言交流. 1.关闭其他标签页(右键菜单触发,保留左侧第一个欢迎tab) 问题:取到的数组tabs长度会随着tab的关闭而变化 思路:先遍历需要关闭的标签页t ...