Spring IOC BeanDefinition解析

IOC(Inversion of Control)即控制反转,是说创建对象的控制权进行了转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权利转移到Spring IOC容器。许多非凡的应用,都是由两个或者多个类通过彼此的合作依赖来实现业务逻辑的,在Spring中,这些依赖关系可以通过把对象的依赖注入交给IOC容器来管理,这样在解耦代码的同时提高了代码的可测试性。

1.    加载bean

加载bean的流程:

(1)     封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。

(2)     获取输入流。从Resource中获取对应的InputStream并构造InputSource。

(3)     通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions。

我们来看一下doLoadBeanDefinitions函数的具体的实现过程(中间省略了loadBeanDefinitions具体方法的一步):

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));
}

继续跟进代码,进入真正的核心处理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource())

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {Document doc = doLoadDocument(inputSource, resource);return registerBeanDefinitions(doc, resource);}catch (BeanDefinitionStoreException ex) {throw ex;}// ……省略异常处理部分}

在上面冗长的代码中,假如不考虑异常类的代码,其实只做了三件事,这三件事的每一件事都必不可少。

(1)     获取对XML文件的验证模式。

(2)     加载XML文件,并得到对应的Document。

(3)     根据返回的Document注册bean信息。

2.    获取XML的验证模式

  protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());}

2.1验证模式的读取

了解XML文件的读者都应该知道XML文件的验证模式保证了XML文件的正确性,而比较常用的验证模式有两种:DTD和XSD。

对于验证模式,读者可以自行查阅数据了解。

验证模式的读取方法如下:

  protected int getValidationModeForResource(Resource resource) {int validationModeToUse = getValidationMode();if (validationModeToUse != VALIDATION_AUTO) {return validationModeToUse;}int detectedMode = detectValidationMode(resource);if (detectedMode != VALIDATION_AUTO) {return detectedMode;}// Hmm, we didn't get a clear indication... Let's assume XSD,// since apparently no DTD declaration has been found up until// detection stopped (before finding the document's root tag).return VALIDATION_XSD;}

方法的实现其实还是很简单的,无非是如果设定了验证模式则使用设定的验证模式,否则使用自动的验证模式,自检测验证模式的功能相对来说比较简单,这里就不再多说了。

3.    获取Document

经过了验证模式准备的步骤就可以进行Document加载了,同样XmlBeanDefinitionReader对于文档的读取并没有亲力亲为,而是委托给了DocumentLoader去执行,解析代码如下(DefaultDocumentLoader中)

   public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isDebugEnabled()) {logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");}DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);}

对于这部分代码其实并没有太多可以描述的,因为通过SAX解析XML文档的套路大致都差不多,Spring在这里并没有什么特殊的地方,同样首先创建DocumentBuilderFactory再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象。

4.    解析及注册BeanDefinitions

把文件转换成Document之后,接下来就可以提取及注册bean了。

  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 在实例化BeanDefinitionReader时候会将BeanDefinitionRegistry传入,默认使用继承自DefaultListableBeanFactory的子类// 记录统计前BeanDefinition的加载个数int countBefore = getRegistry().getBeanDefinitionCount();// 加载及注册bean
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 记录本次加载的BeanDefinition个数return getRegistry().getBeanDefinitionCount() - countBefore;}

进入DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法

   public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();doRegisterBeanDefinitions(root);}

下面进入核心逻辑的底部doRegisterBeanDefinitions(root)方法

  protected void doRegisterBeanDefinitions(Element root) {// 专门处理解析BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {// 处理profile属性String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isInfoEnabled()) {logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}// 解析前处理,留给子类实现
        preProcessXml(root);parseBeanDefinitions(root, this.delegate);// 解析后处理,留给子类实现
        postProcessXml(root);this.delegate = parent;}

4.1profile属性的使用

这个特性可以同时在配置文件中部署两套配置来适用于生产环境和开发环境,这样可以方便的进行切换开发、部署环境,最常用的是更换不同的数据库。

4.2解析并注册BeanDefinitions

处理了profile后就可以进行XML的读取了,跟踪代码进入parseBeanDefinitions(root, this.delegate)

   protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 对beans的处理if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {// 对bean的处理parseDefaultElement(ele, delegate);}else {// 对bean的处理delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}

上面的代码看起来逻辑还是挺清晰的,因为在spring的xml配置里面有两大类bean申明,一个是默认的,一个是自定义的,两种方式的读取及解析差别还是非常大的,如果采用Spring默认的配置,spring当然知道该怎么做,但是如果是自定义的,那么久需要用户实现一些接口及配置了。

对于标签解析,请看我下一篇文章。

转载于:https://www.cnblogs.com/wcj-java/p/9218239.html

Spring IOC BeanDefinition解析相关推荐

  1. Spring Ioc原理解析

    Spring Ioc原理解析 IoC理论的背景 我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑. 图1:软件系统中耦 ...

  2. 第二篇 再读Spring 之 BeanDefinition解析

    第二篇 再读Spring 之 BeanDefinition解析 文章目录 第二篇 再读Spring 之 BeanDefinition解析 一.颗粒度问题 二.细说Spring中不同颗粒度对象在解析中的 ...

  3. spring ioc原理解析

    概述 Spring IOC控制反转,分为两个方面解释: 控制:对象对于内部成员的控制 反转:将之前对象管理自己内部成员,转变为ioc容器管理,目的是接耦 IOC的好处是: 无需手动创建,拿来就用 享受 ...

  4. Spring IOC 原理解析

    Spring IOC 原理 ? IOC DI(依赖注入) **DI注入的几种方式** IOC容器 IOC原理 要想理解IOC,就需要知道一下IOC.DI 和IOC容器都是干什么的 IOC spring ...

  5. Spring IOC和MVC基础知识

    (1)SpringBoot分析 – Spring IOC原理解析 IOC,控制反转 Spring,避免那些可能致使代码变得繁杂.混乱的大量业务工具组合在一起,包括它的创建与销毁,等等,会帮助你进行管理 ...

  6. spring源码解析之IOC核心体系结构

    文章目录 1.spring IOC核心体系结构 1.1 BeanFactory 1.2 BeanDefinition 2.IOC容器初始化 2.1 XmlBeanFactory(屌丝IOC)流程 2. ...

  7. 源码深度解析之 Spring IOC

    这篇文章主要讲解 IOC 容器的创建过程,让你对整体有一个全局的认识,文章没有复杂嵌套的 debug 流程,相对来说比较简单. 不 BB,上文章目录. 1. 基础知识 1.1 什么是 Spring I ...

  8. 源码深度解析系列之 Spring IOC

    大家好,我是黎杜,今天和大家聊聊Spring IOC的源码! 历时 4 个多月,终于把 Sping 源码系列写完了,该系列一共 5 篇,后续会整理成 PDF 教程,本文是最后一篇. 这篇文章主要讲解 ...

  9. 10天,几万字,源码深度解析之 Spring IOC

    历时 10 天,终于把 Sping 源码系列写完了,该系列一共 5 篇,后续会整理成 PDF 教程,本文是最后一篇. 这篇文章主要讲解 IOC 容器的创建过程,让你对整体有一个全局的认识,文章没有复杂 ...

最新文章

  1. 【CVPR 2020】CVPR2020 最新论文下载!看计算机视觉2020在研究什么?
  2. mac开启sshserver
  3. flutter 调用原生安卓插件_Flutter 如何调用Android和iOS原生代码
  4. php验证用户名密码是否为空,【后端开发】php验证用户名密码是否为空
  5. java 内部类异常_Java笔记(三 内部类、异常、编程规范)
  6. Remoting系列专题---构建Remoting“防火墙”
  7. android java显示_Android Studio没有显示java类源代码
  8. STL之 set简略介绍。
  9. 电磁波传播相位是否会变化_民众纷纷转向网上购物,却又担心包裹信件是否会传播病毒?!...
  10. 技术研究院003---六种方式,教你在SpringBoot初始化时搞点事情!
  11. But how to do it? How to avoid direct competition with giants?
  12. pytorch图像预处理
  13. 温暖的《北国之恋》_ing
  14. Linux权限管理命令
  15. 统计函数应用之SQL 分析函数
  16. Vue入门之常用指令
  17. 【触发器】数据库_触发器实例
  18. Word:一级标题行首段前距失效问题
  19. python 猿编程模块(一)ybc_speech
  20. 高盛报告:人工智能、机器学习和数据将推动未来生产力的发展

热门文章

  1. 第50课 书香阁的座位数
  2. RTX5 | 线程管理04 - 线程加入osThreadJoin
  3. Java学习之连接数据库
  4. 【ES6(2015)】Function函数
  5. 在html标签中写三元运算符,如何在剃须刀中使用三元运算符(特别是在HTML属性上)?...
  6. 数据库MySQL的运行情况主要是检查什么_mysql 运行状态分析 运行故障排查实例
  7. PHP笔记-自定义MVC框架
  8. Spring Boot文档阅读笔记-EhCache的使用
  9. MySQL入门之视图
  10. chararray遍历_数组常用的遍历方法 ——总结