Spring IoC源码解读——谈谈bean的几种状态
阅读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的几种状态的更多相关文章
- Spring Ioc源码分析系列--Bean实例化过程(一)
Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...
- Spring Ioc源码分析系列--Bean实例化过程(二)
Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...
- spring beans源码解读之--Bean的注解(annotation)
随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...
- spring beans源码解读之--Bean的定义及包装
bean的定义,包装是java bean的基础.再怎么强调它的重要性都不为过,因此深入 了解这块的代码对以后的代码研究可以起到事半功倍的功效. 1. Bean的定义BeanDefinition 1.1 ...
- spring beans源码解读之--bean definiton解析器
spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean ...
- Spring Ioc源码分析系列--容器实例化Bean的四种方法
Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...
- Spring Ioc源码分析系列--@Autowired注解的实现原理
Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...
- Spring Ioc源码分析系列--自动注入循环依赖的处理
Spring Ioc源码分析系列--自动注入循环依赖的处理 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到Spring创建bean出现循环依赖的时候并没有深入去分 ...
- 深入Spring IOC源码之ResourceLoader
在<深入Spring IOC源码之Resource>中已经详细介绍了Spring中Resource的抽象,Resource接口有很多实现类,我们当然可以使用各自的构造函数创建符合需求的Re ...
随机推荐
- Liferay7 BPM门户开发之47: 集成Activiti待办已办任务清单和流程启动
首先增加两个Portlet,分别用于待办处理.流程启动.待办是别人发起的流程,流到自己这里的流程:流程启动用于发起新的流程. 程序文件放置于 在ACtivit中待办概念分两种,1是指派给你的,专门的指 ...
- Spring.NET的AOP怎么玩
之前公司一直不让使用第三方组件,因此AOP方面的组建一直不能使用,很多面向切面的应用只能通过自己写一些GenericMethod的泛型方法来解决,有一些呆板.由于公司已经开始全面转Java,因此架构组 ...
- KendoUI系列:MultiSelect
1.基本使用 1>.创建Input <link href="@Url.Content("~/Content/kendo/2014.1.318/kendo.common. ...
- 前端学PHP之基础语法
× 目录 [1]代码标识 [2]注释 [3]输出 [4]计算表达式 [5]大小写 [6]空白符 前面的话 PHP是一种创建动态交互性站点的强有力的服务器端脚本语言.PHP能够包含文本.HTML.CSS ...
- codeforces——Little Pony and Expected Maximum
/* 我们枚举每次选择最大数值的情况:m个数, 投掷n次 最大值是1: 1种 2: 2^n-1 3: 3^n-2^n ..... m: m^n-(m-1)^n 所以最后的结果=sum((k/m)^n ...
- python--基础学习(三)字符串单引号、双引号、三引号
1.基本认识 单引号字符串:'python' 双引号字符串:"python" 三引号字符串:'''python'''(三单引号),"""python& ...
- Android网格视图(GridView)
GridView的一些属性: 1.android:numColumns=”auto_fit” //GridView的列数设置为自动,也可以设置成2.3.4…… 2.android:columnWi ...
- RPC原理详解
RPC 功能目标 RPC 的主要功能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性. 为实现该目标,RPC 框架需提供一种透明调用机制让使用者不必显式的区分 ...
- java并发编程实战学习(3)--基础构建模块
转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...
- 手机端web学习基础--from慕课网
web知识零零散散的知道一些,但总感觉不够系统,遇到问题不知道如何解决,因此特此来系统的学习一下web前端的知识.从慕课网的web基础看起.下面学习http://www.imooc.com/learn ...