二、Spring之解析及注册 BeanDefinitions 前期准备

一、案例

public class TestDemo {
	public static void main(String[] args) {
		// 将 xml 转换成 Resource
		Resource resource = new ClassPathResource("spring-dev.xml");
		// 这个类提供加载及注册 bean 的功能
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		// 这个类提供验证、解析 xml 的功能 
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		// 验证及解析 xml、注册及获取 bean
		reader.loadBeanDefinitions(resource);
		// 获取bean
		TestClass testClass = (TestClass)factory.getBean("testClass");
	}
}

二、调用流程解析

1.将 XML 转化成 Resource

Spring 实现了对配置文件的封装,直接用 new ClassPathResource("spring-dev.xml") 即可

2.初始化 XmlBeanDefinitionReader

XmlBeanDefinitionReader 实现对资源文件Resource)进行读取和注册

3.验证及解析 xml、注册及获取 bean

调用流程如下:
  • 1.XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)
    • 2.XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)
      • 3.XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        • 4.1 XmlBeanDefinitionReader#doLoadDocument(InputSource inputSource, Resource resource)
          • 4.1.1 XmlBeanDefinitionReader#loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
        • 4.2 XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource)
          • 4.2.1 DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
            • 4.2.1.1 DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element root)
              • 4.2.1.1.1 DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
流程解析:
  • XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)
    • XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)

上面两个方法处理过程如下:

  1. 封装资源文件。当进入 XmlBeanDefinitionReader 后首先对参数 Resource 使用 EncodedResource 类进行封装。
  2. 获取输入流。从 Resource 中获取对应的 InputStream 并构造 InputSource。
  3. 通过构造的 InputSource 实例 和 Resource 实例继续调用函数 doLoadBeanDefinitions(inputSource, encodedResource.getResource())
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"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 {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}
  • XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource)

上面方法的处理过程如下:

  1. 获取对 XML 文件的验证模式
  2. 加载 XML 文件,并得到对应的 Document
  3. 根据返回的 Document 注册 Bean 信息
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			// 功能一:获取对XML文件的验证模式
			// 功能二:加载 XML 文件,并得到对应的 Document
			Document doc = doLoadDocument(inputSource, resource);
			// 根据返回的 Document 注册 Bean 信息
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		.....
	}
  • XmlBeanDefinitionReader#doLoadDocument(InputSource inputSource, Resource resource)
    • XmlBeanDefinitionReader#loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
      利用 SAX 解析 XML 获取 Document
	/**
	 * 通过 SAX 解析 XML 文档
	 *
	 * @param entityResolver
	 *
	 *     如果 SAX 应用程序需要实现自定义处理外部实体,则必须实现此接口并使用 setEntityResolver() 方法向 SAX 驱动器
	 * 注册一个实例。也就是说,对于解析一个 XML ,SAX首先读取该 XML 文档上的声明,根据声明区寻找相应的DTD定义,以便于
	 * 对文档进行验证。默认的寻找规则即通过网络(实际上就是申明的 DTD 的 URI 地址)来下载相应的 DTD 声明,并进行认证。
	 * 下载的过程是一个漫长的过程,而且当网络中断或不可用时,这里会报错,就是因为相应的 DTD 声明没有被找到的原因。
	 *     EntityResolver 的作用是项目本身可以提供一个如何寻找 DTD 声明的方法,即由程序来实现寻找 DTD 声明的过程,比
	 * 	如我们将 DTD 文件放到项目中某处,在实现时直接将此文档读取并返回给 SAX 即可。这样避免了通过网络来寻找相应的声明。
	 *
	 * 	DTD文件对应的EntityResolver:BeansDtdResolver
	 * 	XSD文件对应的EntityResolver:PluggableSchemaResolver
	 *
	 * 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 {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isTraceEnabled()) {
			logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);
	}
  • XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource)
    • DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
      • DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element root)
        • DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
	/**
	 * XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource)
	 * 根据返回的 Document 注册 Bean 信息
	 */
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		// 使用 DefaultBeanDefinitionDocumentReader 实例化 BeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		// 记录统计前 BeanDefinition 的加载个数
		int countBefore = getRegistry().getBeanDefinitionCount();
		// 加载及注册 Bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		// 记录本次加载的 BeanDefinition 个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
	/**
	 * DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
	 * 这个方法的重要目的之一就是提取root,以便于再次将 root 作为参数继续 BeanDefinition 的注册
	 */
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		/**
		 * Element root = doc.getDocumentElement()
		 *     这个方法的重要目的之一就是提取root,以便于再次将 root 作为参数继续 BeanDefinition 的注册
		 */
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
	/**
	 * DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element root)
	 * Register each bean definition within the given root {@code <beans/>} element.
	 */
	@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
	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.
		// 专门处理解析
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			// 处理 profile 属性
			/*
			 *     首先程序会获取 beans 节点是否定义了 profile 属性,若定义了则会需要到环境变量中去寻找。因为 profile 是
			 * 可以同时指定多个的,需要程序对其拆分,并解析每个 profile 是都符合环境变量中所定义的,不定义则不会浪费性能
			 * 去解析。
			 */
			String profileSpec = 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.
				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;
				}
			}
		}
		// 解析前处理,留给子类
		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		// 解析前处理,留给子类
		postProcessXml(root);

		this.delegate = parent;
	}
	/**
	 * DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
	 * 解析配置文件中的 默认标签 和 自定义标签
	 */
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// 对 beans 处理
		/*
		 * Spring 的 XML 配置里有两大类 Bean 声明,一个是默认的,如:
		 * <bean id="test" class="test.TestBean" />
		 * 另一类是自定义的,如:
		 * <tx:annotation-driven />
		 *     两种方式的读取及解析差别是非常大的,如果采用 Spring 默认的配置,Spring 当然知道该怎么做,但是如果是自定义
		 * 的。那么就需要用户实现一些接口及配置了。对于根节点或者子节点如果是默认命名空间的话采用 parseDefaultElement()
		 * 方法进行解析,否则使用 delegate.parseCustomElement() 方法对自定义命名空间进行解析。而判断是否默认命名空间还是
		 * 自定义命名空间的办法其实是使用 node.getNamespaceURI() 获取命名空间,并与 Spring 中固定的命名空间
		 * http://www.springframework.org/schema/beans 进行对比。如果一致则认为是默认,否则就认为是自定义。
		 */
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
发布了444 篇原创文章 · 获赞 113 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/panchang199266/article/details/100797288
今日推荐