笔记整理自《Spring源码深度解析》(第2版),同时也参考了一些网上资源,具体参考链接在文末
文章目录
核心类介绍
DefaultlistableBeanFactory
DefaultListableBeanFactmy 是整个 bean 加载的核心部分,是 Spring 注册及加载bean的默认实现
图中各个类的作用:
- AliasRegistry:定义对alias的简单增删改等操作。
- SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现。
- SingletonBeanRegistry:定义对单例的注册及获取。
- BeanFactory:定义获取bean及bean的各种属性。
- DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry各函数的实现。
- HierarchicalBeanFactory:继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的支持
- BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作
- FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对FactoryBean的特殊处理功能
- ConfigurableBeanFactory:提供配置Factory的各种方法
- ListableBeanFactory:根据各种条件获取bean的配置清单。
- AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能。
- AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器
- AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现。
- ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等。
- DefaultListableBeanFactory:综合上面所有功能,主要是对bean注册后的处理。
XmlBeanFactory对 DefaultListableBeanFactory类进行了扩展,主要用于从XML文档中读取BeanDefinition,对于注册及获取bean都是使用从父类DefaultListableBeanFactory继承的方法去实现,而唯独与父类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性。在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。
XmlBeanDefinitionReader
实现XML配置文件读取功能
各个类的功能
- ResourceLoader:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
- BeanDefinitionReader:主要定义资源文件读取并转换为BeanDefinition的各个功能。
- EnvironmentCapable:定义获取Environment方法。
- DocumentLoader:定义从资源文件加载到转换为Document的功能。
- AbstractBeanDefinitionReader:对EnvironmentCapable,BeanDefinitionReader类定义的功能进行实现。
- BeanDefinitionDocumentReader:定义读取Docuemnt并注册BeanDefinition功能。
- BeanDefinitionParserDelegate:定义解析Element的各种方法。
处理步骤
1、通过继承自AbstractBeanDefinitionReader中的方法,来使用ResourLoader将资源文件路径转换为对应的Resource文件。
2、通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件。
3、通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析
容器的基础XmlBeanFactory
分析以下功能代码实现
BeanFactory bf=newXmlBeanFactory(newClassPathResource("be anFactoryTest.xml"));
整体分析
XmlBeanFactory初始化
- 用 ClassPathResource的构造函数来构造 Resource 资源文件的实例对象,这样后续的资源处理就可以用 Resource 提供的各种服务来操作> 在Java中,将不同来源的资源抽象成URL,通过注册不同的handler来处理不同来源的资源的读取逻辑,但是URL没有默认定义相对Classpath或ServletContext等资源的handler,且也没有提供基本的方法,比如检查当前资源是否存在,检查当前资源是否可读的等方法。所以Spring使用Resource接口封装底层资源。
- 使用Resouce来进行XmlBeanFactory的初始化
- 利用XmlBeanDefinitionReader来获取beanDefinition
加载bean
接下来我们分析如何获取beanDefinition
处理过程:
- 封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。
- 获取输入流。从Resource中获取对应的InputStream并构造InputSource.
- 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions
源码分析
起始
BeanFactory bf=newXmlBeanFactory(newClassPathResource("be anFactoryTest.xml"));//(1)
(1)构造 Resource 资源文件
ClassPathResource.java
publicClassPathResource(String path){this(path,(ClassLoader)null);}publicClassPathResource(String path,@NullableClassLoader classLoader){Assert.notNull(path,"Path must not be null");String pathToUse =StringUtils.cleanPath(path);if(pathToUse.startsWith("/")){
pathToUse = pathToUse.substring(1);}this.path = pathToUse;this.classLoader =(classLoader !=null? classLoader :ClassUtils.getDefaultClassLoader());}
(2)使用Resouce来进行XmlBeanFactory的初始化
XmlBeanDefinitionReader.java
publicXmlBeanFactory(Resource resource)throwsBeansException{this(resource,null);}//构造函数内部再次调用内部构造函数:publicXmlBeanFactory(Resource resource,BeanFactory parentBeanFactory)throwsBeansException{super(parentBeanFactory);//加载数据在这里完成this.reader.loadBeanDefinitions(resource);//(3)}
(3) 获取beanDefinition
XmlBeanDefinitionReader.java
publicintloadBeanDefinitions(Resource resource)throwsBeanDefinitionStoreException{returnloadBeanDefinitions(newEncodedResource(resource));//(2)}
EncodedResource用于对资源文件的编码进行处理,当设置了编码属性后Spring会根据相应的编码作为输入流的编码
(4) loadBeanDefinitions
XmlBeanDefinitionReader.java
publicintloadBeanDefinitions(EncodedResource encodedResource)throwsBeanDefinitionStoreException{...//从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputstreamtry(InputStream inputStream = encodedResource.getResource().getInputStream()){//通过inputStream获取inputSourceInputSource inputSource =newInputSource(inputStream);//存在编码要求,进行编码设置if(encodedResource.getEncoding()!=null){
inputSource.setEncoding(encodedResource.getEncoding());}//真正进入了逻辑核心部分returndoLoadBeanDefinitions(inputSource, encodedResource.getResource());//(3)}...}
(5)进入逻辑核心部分
XmlBeanDefinitionReader.java
- 获取对XML文件的验证模式。
- 加载XML文件,并得到对应的Documento
- 根据返回的Document注册Bean信息
protectedintdoLoadBeanDefinitions(InputSource inputSource,Resource resource)throwsBeanDefinitionStoreException{...//加载对应的DocumentDocument doc =doLoadDocument(inputSource, resource);//(6)//根据doc注册Bean信息int count =registerBeanDefinitions(doc, resource);//(8)...}
(6)获取Xml的验证模式
XmlBeanDefinitionReader.java
protectedDocumentdoLoadDocument(InputSource inputSource,Resource resource)throwsException{//getValidationModeForResource获取Xml的验证模式 returnthis.documentLoader.loadDocument(inputSource,getEntityResolver(),this.errorHandler,getValidationModeForResource(resource),isNamespaceAware());//(7)}
XML中有两种验证模式:DTD、XSD,保证XML文件的正确性
Spring 中
XmlValidationModeDetector
对验证模式的确认是循环 xml 整个文件的每一行判断是否有
DOCTYPE
字符串, 包含就是 DTD 验证模式, 不包含就是 XSD 模式
(7)获取Document
Spring 中
DocumentLoader
有且仅有一个实现类
DefaultDocumentLoader
DefaultDocumentLoader.java
publicDocumentloadDocument(InputSource inputSource,EntityResolver entityResolver,ErrorHandler errorHandler,int validationMode,boolean namespaceAware)throwsException{//创建DocumentBuilderFactoryDocumentBuilderFactory factory =createDocumentBuilderFactory(validationMode, namespaceAware);if(logger.isTraceEnabled()){
logger.trace("Using JAXP provider ["+ factory.getClass().getName()+"]");}//创建DocumentBuilderDocumentBuilder builder =createDocumentBuilder(factory, entityResolver, errorHandler);//解析inutSource,返回Document对象return builder.parse(inputSource);}
当我们将文件转换成Document后,就到接下来提取及注册bean了,我们回到(5),可以看到接下来要调用registerBeanDefinitions
protectedintdoLoadBeanDefinitions(InputSource inputSource,Resource resource)throwsBeanDefinitionStoreException{...//加载对应的DocumentDocument doc =doLoadDocument(inputSource, resource);//根据doc注册Bean信息,并获取数量int count =registerBeanDefinitions(doc, resource);...}
(8) 解析及注册BeanDefinitions
XmlBeanDefinitionReader.java
publicintregisterBeanDefinitions(Document doc,Resource resource)throwsBeanDefinitionStoreException{// 获取 基于 Document 的Bean定义读取器BeanDefinitionDocumentReader documentReader =createBeanDefinitionDocumentReader();// 历史已有的bean定义数量int countBefore =getRegistry().getBeanDefinitionCount();// 注册bean定义
documentReader.registerBeanDefinitions(doc,createReaderContext(resource));// (9)// 注册后的数量-历史数量returngetRegistry().getBeanDefinitionCount()- countBefore;}
BeanDefinitionDocumentReader
的作用就是进行 BeanDefinition 的注册
(9) 注册bean定义
DefaultBeanDefinitionDocumentReader.java
publicvoidregisterBeanDefinitions(Document doc,XmlReaderContext readerContext){this.readerContext = readerContext;//Spring 进行 Document 对象解析, 并将解析结果包装成 BeanDefinition 进行注册的核心方法doRegisterBeanDefinitions(doc.getDocumentElement());//(10)}
前面都是XML加载解析的准备流程,接下来终于开始进行解析了
(10)doRegisterBeanDefinitions 流程
DefaultBeanDefinitionDocumentReader.java
protectedvoiddoRegisterBeanDefinitions(Element root){// 父 BeanDefinitionParserDelegate 一开始为nullBeanDefinitionParserDelegate parent =this.delegate;// 创建 BeanDefinitionParserDelegatethis.delegate =createDelegate(getReaderContext(), root, parent);// 判断命名空间是否为默认的命名空间// 默认命名空间: http://www.springframework.org/schema/beansif(this.delegate.isDefaultNamespace(root)){// 获取 profile 属性String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);// 是否存在 profileif(StringUtils.hasText(profileSpec)){// profile 切分后的数据String[] specifiedProfiles =StringUtils.tokenizeToStringArray(
profileSpec,BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if(!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)){return;}}}// 前置处理preProcessXml(root);// bean definition 处理parseBeanDefinitions(root,this.delegate);// 后置 xml 处理postProcessXml(root);this.delegate = parent;}
流程分析:
- 设置父
BeanDefinitionParserDelegate
对象, 值得注意的是这个设置父对象一般情况下是不存在的即this.delegate = null
- 创建
BeanDefinitionParserDelegate
对象 ,BeanDefinitionParserDelegate
对象是作为解析的重要方法. - 对于
profile
属性的处理 - XML 解析的前置处理
- XML 的解析处理
- XML 解析的后置处理
- 设置成员变量
补充:
- 这里提一个拓展点
profile
这个属性在 Spring 中一般用来做环境区分, 在 SpringBoot 中有一个类似的配置spring.profiles
. 在 Spring XML 模式中profile
是属于<beans/>
的一个属性preProcessXml(root)
、postProcessXml(root)
这两个方法是为子类而设计的,为模版方法模式,如果需要在Bean解析前后做一些处理的话,那么只需要重写这两个类就可以了
(11)parseBeanDefinitions 分析
DefaultBeanDefinitionDocumentReader.java
protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){// 是否是默认的命名空间if(delegate.isDefaultNamespace(root)){// 子节点列表NodeList nl = root.getChildNodes();for(int i =0; i < nl.getLength(); i++){Node node = nl.item(i);if(node instanceofElement){Element ele =(Element) node;// 是否是默认的命名空间if(delegate.isDefaultNamespace(ele)){// 处理标签的方法parseDefaultElement(ele, delegate);}else{// 处理自定义标签
delegate.parseCustomElement(ele);}}}}else{// 处理自定义标签
delegate.parseCustomElement(root);}}
parseBeanDefinitions
方法主要是对一个
Element
的每个节点进行处理, 节点本身又存在多样性,
- 节点的多样性1. Spring 提供的标签: 即 DTD 或者 XSD 中定义的标签 如:
<bean id="test" class="test.TestBean"/>
2. 自定义标签 如:<tx:anntationo-draiven/>
根据节点多样性 Spring 提供了两个方法进行处理
parseDefaultElement
和
delegate.parseCustomElement(ele)
参考链接:
第三章 IoC 资源读取及注册
版权归原作者 一定会去到彩虹海的麦当 所有, 如有侵权,请联系我们删除。