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 内部调用的图先


  • 相关阅读:
    hugo搭建个人博客
    docker安装mongo
    java+vue跨域每次请求获取不同session问题
    优雅的使用JdbcTemplate
    docker布署springcloud无法使用feign通信
    xxl-job不兼容graylog解决方案
    Springboot集成graylog
    Springboot集成swagger和knife
    Springboot集成xxl-Job
    Springboot中redisTemplate乱码或json转换问题
  • 原文地址:https://www.cnblogs.com/shisw/p/3641114.html
Copyright © 2011-2022 走看看