// Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
/** * 解析Spring配置文件,并把xml中的标签封装成BeanDefinition对象 */ @Override protectedvoidloadBeanDefinitions(DefaultListableBeanFactory beanFactory)throws BeansException, IOException { // 1.为指定BeanFactory创建XmlBeanDefinitionReader //创建xml的解析器,这里是一个委托模式 // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's // resource loading environment. // 2.使用此上下文的资源加载环境配置 XmlBeanDefinitionReader beanDefinitionReader.setEnvironment(this.getEnvironment());
// Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); //3. 加载beanBeanDefinition定义 主要看这个方法 重要程度 5 loadBeanDefinitions(beanDefinitionReader); }
/** * 加载BeanDefinition 将资源文件转换为流并开始真正的处理配置文件 * */ publicintloadBeanDefinitions(EncodedResource encodedResource)throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); }
protectedintgetValidationModeForResource(Resource resource) { intvalidationModeToUse= getValidationMode(); // 1.1 如果手动指定了XML文件的验证模式则使用指定的验证模式 if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } // 1.2 如果未指定则使用自动检测 intdetectedMode= detectValidationMode(resource); // 1.3 如果检测出的验证模式不为 VALIDATION_AUTO, 则返回检测出来的验证模式 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). // 1.4 如果最终没找到验证模式,则使用 XSD return VALIDATION_XSD; }
protectedintdetectValidationMode(Resource resource) { // 1.2.1 校验resource是否为open stream 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 { // 1.2.2 校验resource是否可以打开InputStream 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 { // 1.2.3 根据inputStream检测验证模式 returnthis.validationModeDetector.detectValidationMode(inputStream); } catch (IOException ex) { thrownewBeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex); } }
publicintdetectValidationMode(InputStream inputStream)throws IOException { // Peek into the file to look for DOCTYPE. BufferedReaderreader=newBufferedReader(newInputStreamReader(inputStream)); try { booleanisDtdValidated=false; String content; // 1.2.3.1 按行遍历xml配置文件,获取xml文件的验证模式 while ((content = reader.readLine()) != null) { content = consumeCommentTokens(content); // 如果读取的行是空或者注释则略过 if (this.inComment || !StringUtils.hasText(content)) { continue; } // 内容包含"DOCTYPE"则为DTD,否则为XSD if (hasDoctype(content)) { isDtdValidated = true; break; } // 如果content带有 '<' 开始符号,则结束遍历。因为验证模式一定会在开始符号之前,所以到此可以认为没有验证模式 if (hasOpeningTag(content)) { // End of meaningful data... break; } } // 1.2.3.2 根据遍历结果返回验证模式是 DTD 还是 XSD 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(); } }
/** * 真正开始解析DOM注册BeanDefinition */ @SuppressWarnings("deprecation")// for Environment.acceptsProfiles(String...) 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; //1. 创建BeanDefinition的解析器,交给具体类执行 又是个委托模式 this.delegate = createDelegate(getReaderContext(), root, parent);
// 1.校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans) if (this.delegate.isDefaultNamespace(root)) { // 2.处理profile属性 StringprofileSpec= root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. // 校验当前节点的 profile 是否符合当前环境定义的, 如果不是则直接跳过, 不解析该节点下的内容 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //3. 空方法 解析之前的扩展点用来让子类重写来 preProcessXml(root); //4. 主要看这个方法,标签具体解析过程,解析并注册bean定义 parseBeanDefinitions(root, this.delegate); //5. 空方法解析完成后的扩展点让子类重写来 postProcessXml(root); //返回解析类 this.delegate = parent; }