spring IoC源码分析 (3)Resource解析
来源:互联网 发布:营运数据分析怎样写 编辑:程序博客网 时间:2024/06/05 00:54
定义好了Resource之后,看到XmlFactoryBean的构造函数
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}
跟到XmlBeanDefinitionReader 的 loadBeanDefinitions(EncodedResource encodedResource) 方法
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()); } //线程安全 ,但这里 currentResources应该本来就是线程安全的,所以推测不是为了线程安全 //应该是为了线程能使用同一个 currentResources ,从这里可以看出作者对 ThreadLocal 的理解深刻 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } //这里其实就是为了避免循环加载,如果重复加载了相同的文件就会抛出异常 //这里看了半天才明白这个set的意图,蛋疼啊 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected recursive 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.set(null); } } }其实关键方法是 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) ,但是上面的注释绝对花了我将近1个钟头才理解作者想表达的意思,刚开始一看到ThreadLocal 就想到线程安全,然后就想currentResources 永远是线程安全的啊,丫就这么被带坑里去了。从上面可以看到关键方法是doLoadBeanDefinitions,这个方法的关键代码其实就几行
try { //判断xml文件是DTD还是XSD样式,如果没定义将使用XSD int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); }
看下getValidationModeForResource 一路跟踪下来到XmlValidationModeDetector的detectValidationMode方法
while ((content = reader.readLine()) != null) {content = consumeCommentTokens(content);if (this.inComment || !StringUtils.hasText(content)) {continue;}if (hasDoctype(content)) {isDtdValidated = true;break;}if (hasOpeningTag(content)) {// End of meaningful data...break;}}
就是分析下xml文件里有没有DOCTYPE关键字,没有的话就认为是xsd格式的。然后就到了documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 这个方法
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);}
其实就是使用DocumentBuilderFactory 去解析xml,这块不怎么熟,查了下网上的介绍也没太详细的。
接着跟踪到registerBeanDefinitions 方法,关键部分:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);preProcessXml(root);parseBeanDefinitions(root, delegate);postProcessXml(root);}
preProcessXml和postProcessXml是提供扩展用得,这里没有具体实现,从字面上理解也是给之类提供预处理和事后处理用的。具体解析工作是parseBeanDefinitions,跟踪到private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 这个方法,我们主要关注对bean的解析,所以直接看processBeanDefinition(ele, delegate)
/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {/* BeanDefinitionHolder是BeanDefinition对象的封装类,封装了 BeanDefinition,Bean的名字和别名。用它来完成向IoC容器注册。 BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析*/BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}先来看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 IoC源码分析 (3)Resource解析
- spring IoC源码分析 (2)Resource定位
- Spring源码学习IOC(1):Resource的分析
- Spring IOC源码分析(3)
- Spring IOC 源码分析-bean标签解析
- spring ioc 源码分析 3
- spring ioc 源码解析
- Spring IOC 源码解析
- spring ioc源码解析
- spring源码解析- IOC
- spring ioc源码解析
- Spring 源码解析IoC
- Spring IOC源码分析
- spring ioc 源码解析(一)
- spring ioc 源码解析(二)
- spring源码解析(二)IOC容器
- 深入Spring IOC源码之Resource
- 深入Spring IOC源码之Resource
- 用人单位给计算机系学生的一封信(超长评论版)
- BMP头文件格式以及C语言读取头文件(二)
- 用typedef定义函数指针类型
- external memory algorithm
- 用注册机给keil4 注册 ERROR R206: NO REGISTRY ACCESS, ADMINISTRATION RIGHTS REQUIRED
- spring IoC源码分析 (3)Resource解析
- string char* const char*之间互相转换
- Javascript事件处理机制
- movsx 和movzx
- VC下自建头文件和.c文件
- C++ 中覆写(override) 例解
- 八位二进制数为什么表示范围是:-128~~+127?
- ARM汇编 Init.s,ldr r13,0x1000
- mysql总结之explain