Spring Bean的解析和加载详细解释

spring分为以下几个部分:

  1. Core Container

Core Container 包含 Core,Beans,Context,Expression Language 模块
Core 和 Beans 提供 IOC 和 依赖注入特性.
Context 提供了类似JNDI注册器的框架, ApplicationContext 接口是Context的关键
EL 用于运行时的查询和操作

beans 的解析

  1. 读取Beans

spring自定义资源加载类

Resource resource=new ClassPathResource( "beanFactoryTest .xml ”), 
InputStream inputStream=resource.getinputStream (); 

而资源加载类的底层也是调用字节流的加载,这样可以加载jar中配置文件

if (this . clazz 1= null ) { 
    is = this clazz getRes urceAsStream(this path)
}else { 
    is= this classLoader . getResourceAsStream(this . path} ; 
}

Resources 完成对xml的封装后,将其交给 XMLBeanDefinitionReader

然后 XMLBeanFactory通过调用this.reader.loadBeanDefinitions(resource); 来进行加载Bean

注意:在调用加载资源文件前会先调用 ignore BeanNameAware 接口, BeanNameAware 接口是什么呢? 我们一般用的Bean都是无知觉的,不知道自己在那个工厂中,或者自己的代号,通过实现BeanNameAware接口,接口中就一个方法setBeanName(). A中有属性B, 那么当Spring在获取A的Beans时候,B还没有初始化,Spring会初始化B,但是B实现BeanNameAware接口,就会忽略再进行初始化.
  1. 首先对Resource进行EncodedResource封装, EncodedReasource 作用设置编码属性
    然后使用 SAX 读取 XML 得到Document

  2. 根据Document 注册 解析标签

得到root节点,然后首先处理Profile属性的使用 profile 可以表面使用 dev 还是 production test等,这个特性用来配置生产环境和测试环境.

spring-xml中配置两种Bean声明

<bean id=” test” class=” test TestBean ” />

<tx :annotation-driven/> 

spring中默认的标签有四个:

import alias bean beans

  • bean 标签解析
  1. 提取元素中的id以及name属性
  2. 进一步解析其他属性封装 GenericBeanDefinition中,没有name的生成name

spring的配置信息主要以map形式进行保存

  1. 然后对保存在实体中属性进行解析
<bean id = myTestBea class= "bean.MyTestBean">
    <meta key=” testStr " value= ” aaaaaaaa ” />
</bean>
  1. 解析子元素meta construct, property,qualifier等
    • AbstractBeanDefinition 属性

GenericBeanDefinition 只是子类实现,大部分保存在 AbstractBeanDefinition 中

    • 注册解析的 Bean Definition

解析Bean进行注册主要分为 通过beanName进行注册,通过别名进行注册两种方式

(1) 通过BeanName进行注册

beanDefinitionMap 是全局变量存在并发访问.

beanDefinitionMap beanName作为key, beanDefinition 作为Value

这里会判断如果已经注册同时设置不允许覆盖,则不进行覆盖,否则覆盖 ,清除之前Bean缓存

(2) 通过别名alias进行注册

如果alias与beanName相同不记录alias,使用beanName走1

如果aliasName已经使用并指向另一个beanName进行设置,避免 A->B 存在 , A -> C -> B进行循环检测

注册alias

    • 通知监听器注册完成
  • alias 标签解析

在对bean进行定义,除了使用 id外,还可以使用别名alias,给bean提供多个名称

<bean id= ”testBean” class= ”com.test” />

可以直接当前位置加别名

<bean id= ” testBean ” name= ” testBean , tescBean 2 ” c lass= ” com test " />

同时可以在别处定义bean别名

<alias name="testBean" alias="testBear,testBean2" /〉

和beanName一样都是将别名和beanName组成一对进行注册

  • import 标签解析
<import resource=” customerContext.xml ” />
<import resource=” systemContext.xml ” />

使用import导入不同模块的配置文件

递归调用解析程序

  • 嵌入式beans标签解析

bean 的加载

  1. 转换为对应的beanName

传入参数可能为alias或者FactoryBean ,返回别名指向的最终beanName

  1. 尝试从缓存中加载单例
** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

spring 中有三级缓存,singletonFactories : 单例对象工厂的cache
earlySingletonObjects :提前暴光的单例对象的Cache
singletonObjects:单例对象的cache

spring为了避免循环依赖注入,采用三级缓存,在创建bean中不等bean创建完成就将创建bean的ObjectFactory提前曝光到缓存中,下一个bean需要依赖这个bean就可以直接使用ObjectFactory
  1. bean 的实例化

如果得到的原始状态,就需要对bean进行实例化.
通过工厂获得bean,还未返回bean中factory-method 返回的bean

原型模式需要依赖检测 , 在单例情况下才会尝试解决循环依赖

  1. 如果缓存中没有数据,同时容器中有父类工厂,且不为null,通过父类工厂加载bean
  2. 根据不同scope进行创建bean
  • FactoryBean 的使用

一般情况下通过配置bean的class的反射来实例化bean, 某些情况bean实例比较复杂,spring可以通过实现FactoryBean接口,定制Bean的实例化逻辑

如果要获取工厂的Bean 可以使用 getBean("&car")

  • 缓存中获取单例bean

单例在spring的同一个容器中只会被创建一次,后序再获取bean直接从单利的缓存中获取bean

singletomObjects: 用于保存BeanName 和 创建bean 实例之间的关系

singletonFactories: 用于保存 BeanName 和 Bean工厂之间的关系

earlySingletonObjects :也是保存 BeanName 和创建 bean 实例之间的关系,与
singletonO ects 的不同之处在于,当一个单 bean 被放到这里面后,那么当 bean
在创建过程中,就可以通过 getBean 方法获取到了,其目的是用来检测循环引用

registeredSingletons :用来保存当前所有巳注册的 bean

spring中的循环依赖

(1). 构造器的循环依赖

通过构造器注入构成的循环依赖,此依赖无法解决,抛出异常

<bean id=”testA” class="com.bean.TestA”>
<constructor- arg index="0" ref="testB" /〉
</bean> 
<bean id=”testB” class=” com.bean.TestB”> 
<constructor arg index ="0" ref="testC" /〉
</bean> 
<bean id=”tesi::C ” class=” com.bean.TestC”> 
<constructor- arg index="0" ref="testA" /〉
</bean> 

spring在创建bean的时候会去 "当前正在创建Bean池" ,查找是否正在创建,如果发现依赖正在创建 报循环依赖

(2). setter循环依赖

通过提前暴露一个单例工厂方法,从而使其他 bean 能引用到bean

addSingletonFactory (beanName , new 0:0] ectFactory() { 
    public Object getobject () throws BeansException { 
        return getEarlyBeanReference(beanName,mbd,bean) ; 
    }
});

(3). prototype 范围的依赖处理

对于“prototype ”作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓
存“prototype ”作用域的 bean ,因此无法提前暴露一个创建中的 bean.

  1. 创建Bean

(1) 如果是单例需要首先清除缓存
(2) 实例化Bean ,将BeanDefinition 转换为BeanWrapper
如果工厂使用工厂,有构造函数使用构造,否则默认
(3) MergedBeanDefinitionPostProcessor 应用
bean合并后处理, Autowired 注解通过此方法实现预解析
(4) 依赖处理,属性填充
(5) 注册DisposableBean
(6) 完成并返回

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容

  • Spring源码解析——Bean的加载前奏 User user = (User)context.getBean("...
    仗剑诗篇阅读 868评论 0 2
  • XML解析的准备工作 使用ResourceLoader将资源文件路径转换为Resource文件。封装资源文件。使用...
    一生逍遥一生阅读 2,261评论 0 1
  • 我有一个秘密城堡,那个城堡叫做回忆城堡,在那里,我经过许多的事情都会在这个回忆城堡里一只不停的播放着...
    晓晓_c6df阅读 315评论 0 0
  • 函数就是重复执行的代码片。 // 函数定义 function aa(){ alert('hello...
    紫罗兰丶阅读 145评论 0 0
  • 当年桃园歃血杯, 白龙赤兔伴乌锥。 千里单骑名寰宇, 华容放锁狡枭飞。 力敌曹仁万夫箭, 智取于禁一江水。 孤高气...
    宋宵因阅读 225评论 0 3