zoukankan      html  css  js  c++  java
  • spring IoC源码分析 (3)Resource解析

    引自 spring IoC源码分析 (3)Resource解析

    定义好了Resource之后,看到XmlFactoryBean的构造函数

    1. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {  
    2.         super(parentBeanFactory);  
    3.         this.reader.loadBeanDefinitions(resource);  
    4.     }  
    跟到XmlBeanDefinitionReader 的 loadBeanDefinitions(EncodedResource encodedResource) 方法
    1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
    2.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
    3.         if (logger.isInfoEnabled()) {  
    4.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
    5.         }  
    6.         //线程安全 ,但这里 currentResources应该本来就是线程安全的,所以推测不是为了线程安全  
    7.         //应该是为了线程能使用同一个 currentResources  ,从这里可以看出作者对 ThreadLocal 的理解深刻  
    8.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
    9.         if (currentResources == null) {  
    10.             currentResources = new HashSet<EncodedResource>(4);  
    11.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
    12.         }  
    13.         //这里其实就是为了避免循环加载,如果重复加载了相同的文件就会抛出异常  
    14.         //这里看了半天才明白这个set的意图,蛋疼啊  
    15.         if (!currentResources.add(encodedResource)) {  
    16.             throw new BeanDefinitionStoreException(  
    17.                     "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
    18.         }  
    19.         try {  
    20.             InputStream inputStream = encodedResource.getResource().getInputStream();  
    21.             try {  
    22.                 InputSource inputSource = new InputSource(inputStream);  
    23.                 if (encodedResource.getEncoding() != null) {  
    24.                     inputSource.setEncoding(encodedResource.getEncoding());  
    25.                 }  
    26.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
    27.             }  
    28.             finally {  
    29.                 inputStream.close();  
    30.             }  
    31.         }  
    32.         catch (IOException ex) {  
    33.             throw new BeanDefinitionStoreException(  
    34.                     "IOException parsing XML document from " + encodedResource.getResource(), ex);  
    35.         }  
    36.         finally {  
    37.             currentResources.remove(encodedResource);  
    38.             if (currentResources.isEmpty()) {  
    39.                 this.resourcesCurrentlyBeingLoaded.set(null);  
    40.             }  
    41.         }  
    42.     }  
    其 实关键方法是 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) ,但是上面的注释绝对花了我将近1个钟头才理解作者想表达的意思,刚开始一看到ThreadLocal 就想到线程安全,然后就想currentResources 永远是线程安全的啊,丫就这么被带坑里去了。从上面可以看到关键方法是doLoadBeanDefinitions,这个方法的关键代码其实就几行
    1. try {  
    2.             //判断xml文件是DTD还是XSD样式,如果没定义将使用XSD  
    3.             int validationMode = getValidationModeForResource(resource);  
    4.             Document doc = this.documentLoader.loadDocument(  
    5.                     inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
    6.             return registerBeanDefinitions(doc, resource);  
    7.         }  

    看下getValidationModeForResource 一路跟踪下来到XmlValidationModeDetector的detectValidationMode方法
    1. while ((content = reader.readLine()) != null) {  
    2.                 content = consumeCommentTokens(content);  
    3.                 if (this.inComment || !StringUtils.hasText(content)) {  
    4.                     continue;  
    5.                 }  
    6.                 if (hasDoctype(content)) {  
    7.                     isDtdValidated = true;  
    8.                     break;  
    9.                 }  
    10.                 if (hasOpeningTag(content)) {  
    11.                     // End of meaningful data...  
    12.                     break;  
    13.                 }  
    14.             }  

    就是分析下xml文件里有没有DOCTYPE关键字,没有的话就认为是xsd格式的。然后就到了documentLoader.loadDocument(
    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 这个方法
    1. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,  
    2.             ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {  
    3.   
    4.         DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);  
    5.         if (logger.isDebugEnabled()) {  
    6.             logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");  
    7.         }  
    8.         DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);  
    9.         return builder.parse(inputSource);  
    10.     }  

    其实就是使用DocumentBuilderFactory 去解析xml,这块不怎么熟,查了下网上的介绍也没太详细的。
    接着跟踪到registerBeanDefinitions 方法,关键部分:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  
    2.         this.readerContext = readerContext;  
    3.   
    4.         logger.debug("Loading bean definitions");  
    5.         Element root = doc.getDocumentElement();  
    6.   
    7.         BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);  
    8.   
    9.         preProcessXml(root);  
    10.         parseBeanDefinitions(root, delegate);  
    11.         postProcessXml(root);  
    12.     }  
    preProcessXml和postProcessXml是提供扩展用得,这里没有具体实现,从字面上理解也是给之类提供预处理和事后处理用 的。具体解析工作是parseBeanDefinitions,跟踪到private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)  这个方法,我们主要关注对bean的解析,所以直接看processBeanDefinition(ele, delegate)
    1. /** 
    2.      * Process the given bean element, parsing the bean definition 
    3.      * and registering it with the registry. 
    4.      */  
    5.     protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  
    6. /* BeanDefinitionHolder 是BeanDefinition对象的封装类,封装了 BeanDefinition,Bean的名字和别名。用它来完成向IoC容器注 册。 BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析*/  
    7.         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
    8.         if (bdHolder != null) {  
    9.             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
    10.             try {  
    11.                 // Register the final decorated instance.  
    12.                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
    13.             }  
    14.             catch (BeanDefinitionStoreException ex) {  
    15.                 getReaderContext().error("Failed to register bean definition with name '" +  
    16.                         bdHolder.getBeanName() + "'", ele, ex);  
    17.             }  
    18.             // Send registration event.  
    19.             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
    20.         }  
    21.     }  
    先 来看delegate.parseBeanDefinitionElement(ele) 方法,BeanDefinitionParserDelegate这个类 里包含了对各种Spring Bean定义规则的处理。比如我们最熟悉 的对Bean元素的处理是怎样完成的,也就是怎样处理在XML定义文件中出现的 <bean></bean>这个最常见的元素信息。在这里会看到对那些熟悉的BeanDefinition定义的处 理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读 取出来以后,设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。 对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个 过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到 BeanDefinition对象中并设置到BeanDefinitionHolder中去。其实就是根据spring自己对xml文件的定义进行解析。 这个 BeanDefinition数据对象中封装的数据大多都是与<bean>定义相关的,也有很多就是我们在定义Bean时看到 的那些Spring标记,比如常见的init-method、destroy-method、factory-method,等等,这个 BeanDefinition数据类型是非常重要的,它封装了很多基本数据,这些基本数据都是IoC容器 需要的。有了这些基本数据,IoC容器才能对Bean配置进行处理,才能实现相应的容器特性。spring对资源文件的解析和加载基本到此,下一篇继续分 析spring对bean的获取


    贴一张spring 内部调用的图先


  • 相关阅读:
    DIY 作品 及 维修 不定时更新
    置顶,博客中所有源码 github
    openwrt PandoraBox PBR-M1 极路由4 HC5962 更新固件
    使用 squid 共享 虚拟专用网至局域网
    第一次参加日语能力测试 N5
    libx264 libfdk_aac 编码 解码 详解
    开发RTSP 直播软件 H264 AAC 编码 live555 ffmpeg
    MFC Camera 摄像头预览 拍照
    http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送
    plist 图集 php 批量提取 PS 一个个切
  • 原文地址:https://www.cnblogs.com/shisw/p/3641114.html
Copyright © 2011-2022 走看看