spring分为以下几个部分:
- Core Container
Core Container 包含 Core,Beans,Context,Expression Language 模块
Core 和 Beans 提供 IOC 和 依赖注入特性.
Context 提供了类似JNDI注册器的框架, ApplicationContext 接口是Context的关键
EL 用于运行时的查询和操作
beans 的解析
- 读取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接口,就会忽略再进行初始化.
-
首先对Resource进行EncodedResource封装, EncodedReasource 作用设置编码属性
然后使用 SAX 读取 XML 得到Document -
根据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 标签解析
- 提取元素中的id以及name属性
- 进一步解析其他属性封装 GenericBeanDefinition中,没有name的生成name
spring的配置信息主要以map形式进行保存
- 然后对保存在实体中属性进行解析
<bean id = myTestBea class= "bean.MyTestBean">
<meta key=” testStr " value= ” aaaaaaaa ” />
</bean>
- 解析子元素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 的加载
- 转换为对应的beanName
传入参数可能为alias或者FactoryBean ,返回别名指向的最终beanName
- 尝试从缓存中加载单例
** 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
- bean 的实例化
如果得到的原始状态,就需要对bean进行实例化.
通过工厂获得bean,还未返回bean中factory-method 返回的bean
原型模式需要依赖检测 , 在单例情况下才会尝试解决循环依赖
- 如果缓存中没有数据,同时容器中有父类工厂,且不为null,通过父类工厂加载bean
- 根据不同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.
- 创建Bean
(1) 如果是单例需要首先清除缓存
(2) 实例化Bean ,将BeanDefinition 转换为BeanWrapper
如果工厂使用工厂,有构造函数使用构造,否则默认
(3) MergedBeanDefinitionPostProcessor 应用
bean合并后处理, Autowired 注解通过此方法实现预解析
(4) 依赖处理,属性填充
(5) 注册DisposableBean
(6) 完成并返回
网友评论