看这篇文章以前能够先了解以前的跟踪流程,https://www.jianshu.com/p/4934233f0eadjava
代码过宽,能够shift + 鼠标滚轮 左右滑动查看node
AbstractBeanDefinitionReader类中loadBeanDefinitions方法,该方法会对DOM文档对象进行解析,生成BeanDefinition对象,这篇文章只讲这个方法。web
/** * Load bean definitions from the specified resources. * @param resources the resource descriptors 资源处理器,也叫作资源描述符 * @return the number of bean definitions found 返回发现的bean definition数量 * * 从指定资源中加载bean definitions */ @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; //由于个人web.xml中配置contextConfigLocation无论classpath前缀仍是后半部分 //都没有使用 * 或者 ?等表达式,因此只有一个Resource--资源描述符,或者 //称做资源处理器 for (Resource resource : resources) { //调用的子类XmlBeanDefinitionReader方法,进入查看 counter += loadBeanDefinitions(resource); } return counter; } /** * 此方法在AbstractBeanDefinitionReader的子类XmlBeanDefinitionReader中 * * Load bean definitions from the specified XML file. * 从指定的xml文件中加载bean definitions */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } 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()); } //resourcesCurrentlyBeingLoaded是一个ThreadLocal,里面存放Resource包装类的set集合 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } //若是set中已有这个元素则返回false,进入该条件抛出异常 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( //检测到循环加载某个Resource,须要检查导入的definitions "Detected cyclic 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 { //资源加载完毕,移除该Resource currentResources.remove(encodedResource); //若是集合中已经没有了其余Resource,移除集合 if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
此方法在XmlBeanDefinitionReader类中,分两部分跟踪spring
/** * Actually load bean definitions from the specified XML file. * * 真正的从指定xml文件中加载bean definitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //根据不一样的xml约束(dtd,xsd等),将xml文件生成对应的文档对象 //1.这个方法里面涉及xml的解析 Document doc = doLoadDocument(inputSource, resource); //2.主要就是看这个方法,bean definitions的注册 return registerBeanDefinitions(doc, resource); } //看下抛出异常的提示,都是项目运行时可能出现的错误 catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
跟踪标记1的方法app
此方法在XmlBeanDefinitionReader类中ide
//1.这个方法里面涉及xml的解析 Document doc = doLoadDocument(inputSource, resource); /** * Actually load the specified document using the configured DocumentLoader. * @param inputSource the SAX InputSource to read from --从中读取的SAX输入源 * @param resource the resource descriptor for the XML file --xml文件的资源描述符 * @return the DOM Document DOM文档对象 * * 使用配置好的DocumentLoader文档加载器加载指定的文档 */ protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
上面方法入参说明一下:post
inputSource 由上一层方法传递过来。ui
getEntityResolver() 方法返回 XmlBeanDefinitionReader 类的 entityResolver 属性。
entityResolver 属性在 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 方法中被赋值。this
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this拿的是根web应用上下文,查看ResourceEntityResolver的构造方法编码
/** * Create a ResourceEntityResolver for the specified ResourceLoader * (usually, an ApplicationContext). * @param resourceLoader the ResourceLoader (or ApplicationContext) * to load XML entity includes with * * 为指定的ResourceLoade(一般是应用上下文)r建立一个ResourceEntityResolver */ public ResourceEntityResolver(ResourceLoader resourceLoader) { super(resourceLoader.getClassLoader()); //此处解析器拿到了上下文的引用 this.resourceLoader = resourceLoader; }
调用了父类构造,再跟进一层
/** * Create a new DelegatingEntityResolver that delegates to * a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}. * <p>Configures the {@link PluggableSchemaResolver} with the supplied * {@link ClassLoader}. * @param classLoader the ClassLoader to use for loading * (can be {@code null}) to use the default ClassLoader) */ public DelegatingEntityResolver(ClassLoader classLoader) { //这两个解析器和约束的类型有关,DTD this.dtdResolver = new BeansDtdResolver(); //可插拔的Schema解析器,拿的上下文的类加载器 this.schemaResolver = new PluggableSchemaResolver(classLoader); }
再看doLoadDocument方法的另一个入参 this.errorHandler,这个属性随着XmlBeanDefinitionReader类被初始化而初始化
private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
而后是getValidationModeForResource(resource)入参。
/** * 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. * * 经过给定Resource给出验证模式。若是没有明确配置验证模式,那么调用detectValidationMode方法去检测。 */ protected int getValidationModeForResource(Resource resource) { //默认自动验证,为1 int validationModeToUse = getValidationMode(); //若是有给出具体验证方式,则返回结果 if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } //检测验证模式,进入这个方法 int detectedMode = 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). // 若是实在不能判断验证模式是那种就使用XSD方式, // 由于检测完后仍是没有发现DTD模式的声明(在查找document的根标签以前)。 // 值为3 return VALIDATION_XSD; }
进入下面这个方法,这个方法由XmlBeanDefinitionReader实现
int detectedMode = detectValidationMode(resource); /** * 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. * * 检测执行xml文件时该用哪一种验证方式,这个xml由Resource对象提供 * 若是这个文件有DOCTYPE声明,那么就用DTD验证,不然就假定使用XSD。 * 若是你想要自定义自动验证模式的解决方式,你能够覆盖这个方法 */ protected int detectValidationMode(Resource resource) { //默认false if (resource.isOpen()) { throw new BeanDefinitionStoreException( "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) { throw new BeanDefinitionStoreException( "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 { //进入这个方法查看 return this.validationModeDetector.detectValidationMode(inputStream); } catch (IOException ex) { throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex); } }
XmlBeanDefinitionReader的validationModeDetector属性有默认实现
private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
validationModeDetector调用detectValidationMode方法
/** * 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. * * 在提供的InputStream中检测XML文档的验证模式 * 注意,提供的InputStream在这个方法return以前会被关闭 */ public int detectValidationMode(InputStream inputStream) throws IOException { // Peek into the file to look for DOCTYPE. // 查找文件的DOCTYPE BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { boolean isDtdValidated = false; String content; while ((content = reader.readLine()) != null) { //读一行字符串就干掉字符串里面的注释,若是全是注释全干掉 //主要为了剥离注释,由于非注释内容要么是DOCTYPE声明要么是文档的根元素对象 content = consumeCommentTokens(content); //剥离注释后彻底没内容就继续循环 if (this.inComment || !StringUtils.hasText(content)) { continue; } //有DOCTYPE声明,就跳出去 if (hasDoctype(content)) { isDtdValidated = true; break; } //注释不能进去。开头是"<",后面第一个字符是字母,就进入。 //好比'<beans xmlns="http://www.springframework.org/schema/beans"' //进去后跳出循环 if (hasOpeningTag(content)) { // End of meaningful data... break; } } //当遍历到名称空间了也就是"<beans xmlns=...>"尚未DOCTYPE声明, //那么就断定他为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(); } }
我这么里用的XSD验证。也就是值为3,传递给loadDocument的入参
最后一个入参,isNamespaceAware()
/** * Return whether or not the XML parser should be XML namespace aware. * * XML解析器是否支持XML名称空间 */ public boolean isNamespaceAware() { return this.namespaceAware; }
默认false
看完五个参数后进入正主,loadDocument方法。
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
documentLoader属性默认实现:
private DocumentLoader documentLoader = new DefaultDocumentLoader();
进入DefaultDocumentLoader类的loadDocument方法
/** * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured * XML parser. * * 使用标准JAXP配置XML解析器加载InputSource的Document对象 */ @Override public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //建立文档构建器工厂对象,并初始化一些属性 //若是验证模式为XSD,那么强制支持XML名称空间,并加上schema属性 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } //建立一个JAXP文档构建器 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); //按照XML文档解析给定inputSource的内容,而后返回一个新的DOM对象 return builder.parse(inputSource); }
这样就拿到了Document对象
回到XmlBeanDefinitionReader类的doLoadBeanDefinitions方法
跟踪标记2的方法
//2.主要就是看这个方法,bean definitions的注册 return registerBeanDefinitions(doc, resource); /** * 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. * * 注册包含在给定DOM文档对象中的 bean definition * 被loadBeanDefinitions方法所调用 * 解析class后建立一个新的实例,并调用registerBeanDefinitions方法 */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //getRegistry()方法拿的是bean工厂对象,beanDefinition注册在工厂中 //这个方法就是返回已经被注册在工厂中的beanDefinitions数量 int countBefore = getRegistry().getBeanDefinitionCount(); //进入这个方法查看 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //返回上个方法真正注册在工厂中的beanDefinition数量 return getRegistry().getBeanDefinitionCount() - countBefore; } /** * 这个方法在刚建立的DefaultBeanDefinitionDocumentReader中 * * 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. * * 根据“spring-beans"的XSD(或者DTD)去解析bean definition * 打开一个DOM文档,而后初始化在<beans/>层级上指定的默认设置,而后解析包含在其中的bean definitions */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //入参时建立的XmlReaderContext对象 this.readerContext = readerContext; logger.debug("Loading bean definitions"); //拿到了xml文档对象的根元素 Element root = doc.getDocumentElement(); //进入这个方法进行查看 doRegisterBeanDefinitions(root); }
查看DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法。
/** * Register each bean definition within the given root {@code <beans/>} element. * * 在给定的根元素对象<beans/>中,注册每个bean definition */ protected void doRegisterBeanDefinitions(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. // 任何被嵌套的<beans>元素都会致使此方法的递归。为了正确的传播和保存<beans>的默认属性、 // 保持当前(父)代理的跟踪,它可能为null // 为了可以回退,新的(子)代理具备父的引用,最终会重置this.delegate回到它的初始(父)引用。 // 这个行为模拟了一堆代理,但实际上并不须要一个代理 BeanDefinitionParserDelegate parent = this.delegate; //2.1建立一个新的代理,并初始化一些默认值 this.delegate = createDelegate(getReaderContext(), root, parent); //默认名称空间是"http://www.springframework.org/schema/beans" if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); //个人xml文件中没有设置"profile",因此跳过 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; } } } //xml预处理,子类没有重写里面就是空实现 preProcessXml(root); //2.2生成BeanDefinition,并注册在工厂中 parseBeanDefinitions(root, this.delegate); //xml后处理,子类没有重写里面就是空实现 postProcessXml(root); this.delegate = parent; }
跟踪标记2.1的方法
此方法在DefaultBeanDefinitionDocumentReader类中
入参 getReaderContext() 方法返回的是先前建立的 XmlReaderContext 对象
//2.1建立一个新的代理,并初始化一些默认值 this.delegate = createDelegate(getReaderContext(), root, parent); protected BeanDefinitionParserDelegate createDelegate( XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { //用来解析XML bean definition的有状态代理类,用来被主解析器和其余扩展使用 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); //进入此方法 delegate.initDefaults(root, parentDelegate); return delegate; }
进入BeanDefinitionParserDelegate类的initDefaults方法
/** * Initialize the default lazy-init, autowire, dependency check settings, * init-method, destroy-method and merge settings. Support nested 'beans' * element use cases by falling back to the given parent in case the * defaults are not explicitly set locally. * * 初始化默认值 default :·······等 * 经过使用 parent default,来解决嵌套的'beans'元素状况,以防 default 在局部设定不明确 */ public void initDefaults(Element root, BeanDefinitionParserDelegate parent) { //进入此方法 populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root); //默认没作任何实现 this.readerContext.fireDefaultsRegistered(this.defaults); }
populateDefaults方法跟踪
入参this.defaults有默认实现
private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
第二个入参:当第一次进入此方法时,parent为null。
进入方法
/** * Populate the given DocumentDefaultsDefinition instance with the default lazy-init, * autowire, dependency check settings, init-method, destroy-method and merge settings. * Support nested 'beans' element use cases by falling back to <literal>parentDefaults</literal> * in case the defaults are not explicitly set locally. * * 用默认的值填充DocumentDefaultsDefinition实例 * 经过使用parentDefaults(父代理的default属性),来解决嵌套的'beans'元素状况,以防默认值在局部设定不明确 */ protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) { //根元素上若是没有设定值,则返回"default"字符串 String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE); //若是为"default",先看parentDefaults有没有,有用它的,没有用"false" if (DEFAULT_VALUE.equals(lazyInit)) { // Potentially inherited from outer <beans> sections, otherwise falling back to false. // 可能从外部<beans>继承,不然返回false lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE); } defaults.setLazyInit(lazyInit); String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE); if (DEFAULT_VALUE.equals(merge)) { // Potentially inherited from outer <beans> sections, otherwise falling back to false. merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE); } defaults.setMerge(merge); String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE); if (DEFAULT_VALUE.equals(autowire)) { // Potentially inherited from outer <beans> sections, otherwise falling back to 'no'. autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE); } defaults.setAutowire(autowire); // Don't fall back to parentDefaults for dependency-check as it's no longer supported in // <beans> as of 3.0. Therefore, no nested <beans> would ever need to fall back to it. // 依赖检查不会采用parentDefaults,由于在3.0以后已再也不支持。所以,嵌套的<beans>不会使用parentDefaults defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates()); } if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setInitMethod(parentDefaults.getInitMethod()); } if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setDestroyMethod(parentDefaults.getDestroyMethod()); } //extractSource方法这里没有作任何实现,默认返回null defaults.setSource(this.readerContext.extractSource(root)); }
跟踪标记2.2的方法
在DefaultBeanDefinitionDocumentReader类中
//2.2生成BeanDefinition,并注册在工厂中 parseBeanDefinitions(root, this.delegate); /** * Parse the elements at the root level in the document: * "import", "alias", "bean". * * 解析在文档中根层级的元素:"import", "alias", "bean". */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //默认名称空间是"http://www.springframework.org/schema/beans" //进入条件 if (delegate.isDefaultNamespace(root)) { //获取根元素下的子Node,注意,Node不必定是子标签,多是回车,多是注释 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { //拿到了<beans>下的子标签 Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //3.若是该标签属于beans的名称空间,则进入这个方法 //xmlns="http://www.springframework.org/schema/beans" parseDefaultElement(ele, delegate); } else { //4.若是该标签属于其余的名称空间好比:context,aop等 //xmlns:aop="http://www.springframework.org/schema/aop" //xmlns:context="http://www.springframework.org/schema/context" delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
一个名称空间对应一个处理器。
好比在spring的配置文件applicatinoContext.xml中加入了这行代码:
<!-- 启用注解 --> <context:annotation-config />
能够知道,标签annotation-config属于名称空间context。
那么context就对应着一个处理器。
解析的时候会先获取context对应的处理器。处理器中又会初始化一些解析器出来,一个解析器就对应着一个标签,像annotation-config就有对应本身的解析器。
不一样的名称空间、不一样的标签致使解析的方式很是的多,这里只拿两个常见的配置进行跟踪。
由于字数超过了限制,因此分红了三篇,点击下篇继续阅读
https://www.jianshu.com/p/46e27afd7d96
——————————————————————————————————
beans
默认名称空间的元素解析,和其余自定义元素解析。通常来讲,一个名称空间对应一个处理器,处理器中又会初始化一些解析器出来。一个解析器就对应着一个标签,能够对不一样的状况进行解析。