专栏首页JavaQ深入理解Spring系列之五:BeanDefinition装载

深入理解Spring系列之五:BeanDefinition装载

接上篇《深入理解Spring系列之四:BeanDefinition装载前奏曲》,进入AbstractXmlApplicationContext类的loadBeanDefinitions方法,代码如下所示。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }

关注其中的最后一行的loadBeanDefinitions方法,具体实现也在AbstractXmlApplicationContext类中,代码如下所示。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }

这个方法里就是根据给定的xml配置文件进行后续的解析及装载。继续跟踪代码,进入reader.loadBeanDefinitions(configLocations),这个XmlBeanDefinitionReader用于从xml文件中读取Bean的定义。接下来的代码有很多跳转,大部分都是不重要的,直接追踪到重要代码,代码如下。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); }finally { inputStream.close(); } }catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); }finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }

这里只要关注doLoadBeanDefinitions方法,进入其实现,代码如下。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); }catch (BeanDefinitionStoreException ex) { throw ex; }catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); }catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); }catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); }catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); }catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }

直接看try块中的代码,可以看到,Spring将xml配置文件转成Document,这里使用了SAX对XML的解析,至于里面是如何解析可以后续针对性的研究。接着进入registerBeanDefinitions方法,后续又有很多代码的跳转,先不一一关注,直接进入重要代码,代码如下。

protected void parseBeanDefinitions(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 instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); }else { delegate.parseCustomElement(ele); } } } }else { delegate.parseCustomElement(root); } }

可以看到就是对Document中元素、节点的不断解析。这里的解析分成了两条路线,一个是默认标签的解析,如Spring自己定义的标签;一个是对自定义标签的解析,如自定义的标签。这里先关注默认标签的解析,对于自定义标签的解析可以后续分析,进入parseDefaultElement方法,代码如下。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); }else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); }else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); }else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }

代码中分别对import、alias、bean、beans标签进行解析,最终将解析得到的BeanDefinition存入DefaultListableBeanFactory中的beanDefinitionMap中。

本文分享自微信公众号 - JavaQ(Java-Q),作者:未来先生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-11-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深入Spring Boot (十三):整合Kafka详解

    Kafka是一种高吞吐量的分布式流处理平台,它具有高可用、高吞吐量、速度快、易扩展等特性。本篇将介绍如何使用Spring Boot整合Kafka及使用Kafka...

    JavaQ
  • Java注解是如何玩转的,面试官和我聊了半个小时

    小白:自定义注解后,需要定义这个注解的注解解析及处理器,在这个注解解析及处理器的内部,通过反射使用Class、Method、Field对象的getAnnotat...

    JavaQ
  • 深入浅出JDK动态代理(二)

    接上篇《深入浅出JDK动态代理(一)》 代理类解密 对于JDK动态代理,生成的代理类是什么样的?为什么调用代理类的任何方法时都一定会调用invoke方法?下面来...

    JavaQ
  • SysML 2019论文解读:推理优化

    随着机器学习和人工智能领域的持续发展,神经网络及其代表性的算法通过提升计算成本而实现了越来越高的准确度。量化(quantization)是一种以准确度为代价旨在...

    机器之心
  • python-剑指offer16-20

    输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 1...

    绝命生
  • Vue.js 实战总结

    最近在某个项目中用到了Vue.js,从上手做开发到项目发布,一步步踩了不少坑。本文试图总结过去一个多月使用Vue.js中的一些经验,也算是一点心得体会吧,拿出来...

    徐江伟
  • leetcode 20 Valid Parentheses 括号匹配

    Given a string containing just the characters '(', ')', '{', '}', '[' and']', d...

    流川疯
  • leetcode: 101. Symmetric Tree

    JNingWei
  • 一句话介绍Dubbo架构原理

    什么是微内核? 微内核即最小化内核,内核只负责插件的组装,不带任何功能逻辑,所有功能都由可替换的插件实现。 比如: Spring,OSGI,JMX,Ser...

    MickyInvQ
  • Python 单元测试 增强系统健壮性

    1.JulyNovel需要在request_url插入spider.wait队列之前去判断有没有必要去爬这条url

    从今若

扫码关注云+社区

领取腾讯云代金券