以通过BeanFactory获取bean为例学习,实际项目中更多是使用ApplicationContext
1.读取配置文件 beanFactoryTest.xml
2.通过文件资源获取工厂
3.从工厂中拿bean
# 读配置
# 配置文件封装
ClassPathResource类 继承–AbstractFileResolvingResource
继承–AbstractResource
实现–Resource
继承–InputStreamSource接口 唯一方法: getInputStream()
Resource接口提供了一系列方法来封装底层资源,比如 exists() isReadable() getURL() getFile() getDescription() 等等
# XmlBeanFactory的实例化
# 大概步骤
|
|
loadBeanDefinitions(resource)方法就是下面的主要要看的
它做了几件事:
- 将resource用EncodedResource包装一下,编码要用, 本例没特定的charset和encoding因此都是null
- 获取当前正在加载的资源集 Set
currentResources ,如果currentResources 是null 就初始化个 new HashSet(4), 然后将我们要读的encodedResource加到这个set里面去
如果返回false说明正在加载这个xml了,报个错,不继续往下读了
当读完这个资源后,在finally里面会从set里移出这个encodedResource,如果set空了,还会remove这个currentResources,
这个set是放ThreadLocal里面的,
private final ThreadLocal<Set
> resourcesCurrentlyBeingLoaded; 是线程安全的,功能是防止同时循环加载同一个xml文件
- 从encodedResouce里取出文件的inputStream 拿去初始化个InputSource(有enconding的话,给inputSource也设个编码,本例为null)然后 inputSource和encodedResouce里面的resouce一起拿去执行doLoadBeanDefinitions(..)方法,稍后重点讲
|
|
- 关闭资源, 释放currentResources 等
# 关键:doLoadBeanDefinitions
# 将资源转为Document
|
|
- 获取xml的验证模式 , 里面的核心是自动检测,自动检测的核心是拿着inputStream读,判断是否含有"DOCTYPE",包含就是DTD,没有就是XSD
- 拿到EntityResolver , 这个主要是用来给xml验证时找DTD文件获取路径找得快的, 不用全去spring网站拿dtd,略
- 解析document, 创建DocumentBuilderFactory,通过此工厂创建DocumentBuilder,进而解析inputSource来返回document对象. 此为通过SAX(Simple API for XML)来解析的常见方法,略过
注意到本方法的参数中又用到resource, 明明inputSource就是从resource里拿inputStream生成出来的啊, 相当于inputSource就是儿子, doLoadDocument方法请了儿子又去请爹,烦不烦?
其实主要是之前设计的时候, 获取XML验证模式的自动检测方法,用的是Resource接口,isOpen()方法因此不得不用
isOpen()方法是Resource接口的方法,其实现返回false(比如AbstractResource),有的返回的是true(InputStreamResource).
# 注册bean
|
|
先走流程
|
|
- 创建阅读器documentReader
- 记录加载BeanDefinition的个数,
- documentReader来加载注册doc和封装resource而生成的Context
继续走流程, 在reader内设置context,拿到root元素,重点在最后解析root
|
|
# 开始核心部分 doRegisterBeanDefinitions
|
|
this.preProcessXml(root); this.postProcessXml(root);此处的预留 设计模式—模板方法模式
profile
关于profile属性书中讲用途讲的很清楚,从源码中我了解到
profile可以同时指定多个属性,比如
只需当前环境变量里值满足其中一种就OK 多个属性可以支持多种分隔符 逗号 分号 空格 源码是
1
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
可以用一个感叹号修饰属性 比如
表示"非" , 不可多个感叹号 读取并设置spring.profiles.active属性时还加了sychronized
spring.profiles.active属性值可以多个 ,用逗号隔开
判断过程其实是
1
return currentActiveProfiles.contains(profile) || currentActiveProfiles.isEmpty() && this.doGetDefaultProfiles().contains(profile);
spring.profiles.active里包含这个属性, 或者spring.profiles.active为空,但是spring.profiles.default里包含这个属性
ps.这个属性看明白很有成就感,书上没解释源码, 看到这段之前先是自己埋着头进去钻源码,莫名其妙卡了很久,并不知道整段的功能是啥,继续看书看到此处,了解了这个profile 的功能,再回头结合源码,就恍然大悟:)
判断是不是默认命名空间
|
|
node获取命名空间方法 getNamespaceURI()主要在 rt.jar里有很多种不同的实现,返回字符串,就不再去深究了
# 再深一层 parseBeanDefinitions
不贴源码了,这个方法主要是先判断这个节点是不是默认命名空间 ,不是就按自定义的方式解析,
是的话就遍历节点,解析每个节点.
每个节点解析之前都还会去判断一遍是否默认命名空间,
如果不是就按自定义方式解析,如果是就才按默认方式解析
默认标签如
|
|
自定义标签如
|
|
好,书的第二章到此为止,第三章将继续深入默认标签的解析