/** * Return an {@link InputStream} for the content of an underlying resource. * <p>It is expected that each call creates a <i>fresh</i> stream. * <p>This requirement is particularly important when you consider an API such * as JavaMail, which needs to be able to read the stream multiple times when * creating mail attachments. For such a use case, it is <i>required</i> * that each {@code getInputStream()} call returns a fresh stream. * @return the input stream for the underlying resource (must not be {@code null}) * @throws java.io.FileNotFoundException if the underlying resource doesn't exist * @throws IOException if the content stream could not be opened */ InputStream getInputStream()throws IOException; }
/** * This implementation opens an InputStream for the given class path resource. * @see java.lang.ClassLoader#getResourceAsStream(String) * @see java.lang.Class#getResourceAsStream(String) */ @Override public InputStream getInputStream()throws IOException { InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } elseif (this.classLoader != null) { is = this.classLoader.getResourceAsStream(this.path); } else { is = ClassLoader.getSystemResourceAsStream(this.path); } if (is == null) { thrownewFileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; }
在 XmlFactoryBean 中,我们先调用了 super,现在看看 super 干了什么事情
1 2 3 4 5 6 7 8 9
/** * Create a new AbstractAutowireCapableBeanFactory. */ publicAbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
举例来说,当 A 中有属性 B,那么当 Spring 在获取 A 的 Bean 的时候如果其属性 B 还没有初始化,那么 Spring 会自动初始化 B,这也是 Spring 中提供的一个重要特性。但是,某些情况下,B 不会被初始化,其中的一种情况就是 B 实现了 BeanNameAware 接口。Spring 中是这样介绍的:自动装配时忽略给定的依赖接口,典型应用是通过其他方式解析 Application 上下文注册依赖,类似于 BeanFactory 通过 BeanFactoryAware 进行注入或者 ApplicationContext 通过 ApplicationContextAware 进行注入。
/** * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ publicintloadBeanDefinitions(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 = newHashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { thrownewBeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 从 encodedResource 中获取已经封装的 Resource 对象并再次从 Resource 中获取其中的 inputStream InputStreaminputStream= encodedResource.getResource().getInputStream(); try { // InputSource 这个类并不来自于 Spring,它的全路径是 org.xml.sax.InputSource InputSourceinputSource=newInputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 真正进入逻辑核心部分 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { thrownewBeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
/** * Actually load bean definitions from the specified XML file. * @param inputSource the SAX InputSource to read from * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #doLoadDocument * @see #registerBeanDefinitions */ protectedintdoLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Documentdoc= doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { thrownewXmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { thrownewXmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { thrownewBeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { thrownewBeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { thrownewBeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
/** * Actually load the specified document using the configured DocumentLoader. * @param inputSource the SAX InputSource to read from * @param resource the resource descriptor for the XML file * @return the DOM Document * @throws Exception when thrown from the DocumentLoader * @see #setDocumentLoader * @see DocumentLoader#loadDocument */ protected Document doLoadDocument(InputSource inputSource, Resource resource)throws Exception { returnthis.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
doLoadBeanDefinitions 中做了三件事:
获取对 XML 文件的验证模型
加载 XML 文件,并得到对应的 Document
根据返回的 Document 注册 Bean 信息
获取 XML 的验证模式
DTD 与 XSD 区别
DTD (文档类型定义),保证 XML 文档格式正确的有效方法,可以通过比较 XML 文档和 DTD 文件来看文档是否符合规范,元素和标签使用是否正确。一个 DTD 文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。
XSD (XML Schemas Definition)。XML Scheme 描述了 XML 文档的结构。可以用一个指定的 XML Schema 来验证某个 XML 文档,以检查该 XML 文档是否符合其要求。文档设计者可以通过 XML Schema 指定一个 XML 文档所允许的结构和内容,并可据此检查一个 XML 文档是否是有效的。XML Schema 本身是一个 XML 文档,它符合 XML 语法结构。可以用通用的 XML 解析器解析它。
/** * Gets the validation mode for the specified {@link Resource}. If no explicit * validation mode has been configured then the validation mode is * {@link #detectValidationMode detected}. * <p>Override this method if you would like full control over the validation * mode, even when something other than {@link #VALIDATION_AUTO} was set. */ protectedintgetValidationModeForResource(Resource resource) { intvalidationModeToUse= getValidationMode(); // 如果手动指定了验证模式则使用指定的验证模式 if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } // 如果未指定则使用自动检测 intdetectedMode= 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; }
/** * Detects which kind of validation to perform on the XML file identified * by the supplied {@link Resource}. If the file has a {@code DOCTYPE} * definition then DTD validation is used otherwise XSD validation is assumed. * <p>Override this method if you would like to customize resolution * of the {@link #VALIDATION_AUTO} mode. */ protectedintdetectValidationMode(Resource resource) { if (resource.isOpen()) { thrownewBeanDefinitionStoreException( "Passed-in Resource [" + resource + "] contains an open stream: " + "cannot determine validation mode automatically. Either pass in a Resource " + "that is able to create fresh streams, or explicitly specify the validationMode " + "on your XmlBeanDefinitionReader instance."); }
InputStream inputStream; try { inputStream = resource.getInputStream(); } catch (IOException ex) { thrownewBeanDefinitionStoreException( "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + "Did you attempt to load directly from a SAX InputSource without specifying the " + "validationMode on your XmlBeanDefinitionReader instance?", ex); }
try { returnthis.validationModeDetector.detectValidationMode(inputStream); } catch (IOException ex) { thrownewBeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex); } }
/** * Detect the validation mode for the XML document in the supplied {@link InputStream}. * Note that the supplied {@link InputStream} is closed by this method before returning. * @param inputStream the InputStream to parse * @throws IOException in case of I/O failure * @see #VALIDATION_DTD * @see #VALIDATION_XSD */ publicintdetectValidationMode(InputStream inputStream)throws IOException { // Peek into the file to look for DOCTYPE. BufferedReaderreader=newBufferedReader(newInputStreamReader(inputStream)); try { booleanisDtdValidated=false; String content; 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; } } return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD); } catch (CharConversionException ex) { // Choked on some character encoding... // Leave the decision up to the caller. return VALIDATION_AUTO; } finally { reader.close(); } }
/** * Does the content contain the DTD DOCTYPE declaration? */ privatebooleanhasDoctype(String content) { return content.contains(DOCTYPE); }
获取 Document
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/** * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured * XML parser. */ @Override public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)throws Exception {
/** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ publicintregisterBeanDefinitions(Document doc, Resource resource)throws BeanDefinitionStoreException { // 使用 DefaultBeanDefinitionDocumentReader 实例化 BeanDefinitionDocumentReader BeanDefinitionDocumentReaderdocumentReader= createBeanDefinitionDocumentReader(); // 在实例化 BeanDefinitionReader 时候会将 BeanDefinitionRegistry 传入,默认使用继承 DefaultListableBeanFactory 的子类 // 记录统计前 BeanDefinition 的加载个数 intcountBefore= getRegistry().getBeanDefinitionCount(); // 加载及注册 bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 记录本次加载的 BeanDefinition 个数 return getRegistry().getBeanDefinitionCount() - countBefore; }
/** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. */ @Override publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Elementroot= doc.getDocumentElement(); // 重点:提取 root doRegisterBeanDefinitions(root); }
/** * Register each bean definition within the given root {@code <beans/>} element. */ protectedvoiddoRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. // 专门处理解析 BeanDefinitionParserDelegateparent=this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) { // 处理 profile 属性 StringprofileSpec= 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; }
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 对 beans 的处理 if (delegate.isDefaultNamespace(root)) { NodeListnl= root.getChildNodes(); for (inti=0; i < nl.getLength(); i++) { Nodenode= nl.item(i); if (node instanceof Element) { Elementele= (Element) node; if (delegate.isDefaultNamespace(ele)) { // 默认标签解析 parseDefaultElement(ele, delegate); } else { // 自定义标签解析 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }