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 ...
随机推荐
- Java基础-继承 利用接口做参数,写个计算器,能完成+-*/运算
38.利用接口做参数,写个计算器,能完成+-*/运算 (1)定义一个接口Compute含有一个方法int computer(int n,int m); (2)设计四个类分别实现此接口,完成+-*/运算 ...
- hibernate(九)多对多关联
原文链接:http://www.orlion.ml/29/ 一.多对多单向关联 假设一个老师教多个学生,一个学生被多个老师教,这就是典型的多对多关系 配置方式是在Teacher类的getStudent ...
- jxl读取excel实现导入excel写入数据库
@RequestMapping(params = "method=import", method = RequestMethod.POST) @ResponseBody publi ...
- 多个Jar包的合并操作
原文:http://www.cnblogs.com/meteoric_cry/p/4283656.html 需求是将多个jar合并成一个jar的问题.这里列一下操作步骤: 1.将所有jar文件复制至某 ...
- javase基础复习攻略《七》
容器是什么?通俗的讲容器指可以装其它东西的器皿,前面我们提到的数组便是容器的一种,容器的概念在JAVA中便可以理解为用来存储其它对象的器皿.本篇就让我们一起来认识一下JAVA为我们提供的容器类. 1. ...
- ASP.NET MVC图片管理(上传,预览与显示)
先看看效果(下面gif动画制作有点大,5.71MB): 题外话:上面选择图片来源于Insus.NET的新浪微博:http://weibo.com/104325017 也是昨晚(2015-07-03)I ...
- JavaWeb:实现文件上传
JavaWeb:实现文件上传 理解文件上传: 1.上传文件就是把客户端的文件发送给服务器端. 2.HTTP响应的正文部分最常见的是HTML文档,但是也可以是其他任意格式的数据,如图片和声音文件中的数据 ...
- Web 开发精华文章集锦(jQuery、HTML5、CSS3)【系列二十七】
<Web 前端开发精华文章推荐>2014年第6期(总第27期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...
- HTML5本地存储 Web Storage
Web Storage基本介绍 HTML5 定义了本地存储规范 Web Storage , 提供了两种存储类型 API sessionStorage 和 localStorage,二者的差异主要是数 ...
- 如何用Ajax加载服务器的图片
用Ajax请求服务器的图片,并显示在浏览器中 前言 一直在数据库里面存的都是图片在服务器的地址,然后再到浏览器中显示,但是发现两个问题 第一:为了安全起见,js是无法读取本地的图片的,不然你写一个js ...