1. Spring 源码:Spring 解析XML 配置文件,获得 Bena 的定义信息

通过 Debug 运行 XmlBeanDefinitionReaderTests 类的 withFreshInputStream() 的方法,调试 Spring 解析 XML 配置文件,获得 Bean 的定义。

大体流程可根据序号查看,xml 配置文件随便看一眼,不用过多在意。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>

	<bean id="validEmptyWithDescription" class="org.springframework.beans.testfixture.beans.TestBean">
		<description>
			I have no properties and I'm happy without them.
		</description>
	</bean>

	<!--
		Check automatic creation of alias, to allow for names that are illegal as XML ids.
	-->
	<bean id="aliased" class="  org.springframework.beans.testfixture.beans.TestBean  " name="myalias">
		<property name="name"><value>aliased</value></property>
	</bean>

	<alias name="aliased" alias="youralias"/>

	<alias name="multiAliased" alias="alias3"/>

	<bean id="multiAliased" class="org.springframework.beans.testfixture.beans.TestBean" name="alias1,alias2">
		<property name="name"><value>aliased</value></property>
	</bean>

	<alias name="multiAliased" alias="alias4"/>

	<bean class="org.springframework.beans.testfixture.beans.TestBean" name="aliasWithoutId1,aliasWithoutId2,aliasWithoutId3">
		<property name="name"><value>aliased</value></property>
	</bean>

	<bean class="org.springframework.beans.testfixture.beans.TestBean">
		<property name="name"><null/></property>
	</bean>

	<bean class="org.springframework.beans.factory.xml.DummyReferencer"/>

	<bean class="org.springframework.beans.factory.xml.DummyReferencer"/>

	<bean class="org.springframework.beans.factory.xml.DummyReferencer"/>

	<bean id="rod" class="org.springframework.beans.testfixture.beans.TestBean">
		<property name="name"><value><!-- a comment -->Rod</value></property>
		<property name="age"><value>31</value></property>
		<property name="spouse"><ref bean="father"/></property>
		<property name="touchy"><value/></property>
	</bean>

	<bean id="roderick" parent="rod">
		<property name="name"><value>Roderick<!-- a comment --></value></property>
		<!-- Should inherit age -->
	</bean>

	<bean id="kerry" class="org.springframework.beans.testfixture.beans.TestBean">
		<property name="name"><value>Ker<!-- a comment -->ry</value></property>
		<property name="age"><value>34</value></property>
		<property name="spouse"><ref bean="rod"/></property>
		<property name="touchy"><value></value></property>
	</bean>

	<bean id="kathy" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
		<property name="name"><value>Kathy</value></property>
		<property name="age"><value>28</value></property>
		<property name="spouse"><ref bean="father"/></property>
	</bean>

	<bean id="typeMismatch" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
		<property name="name"><value>typeMismatch</value></property>
		<property name="age"><value>34x</value></property>
		<property name="spouse"><ref bean="rod"/></property>
	</bean>

	<!-- Test of lifecycle callbacks -->
	<bean id="mustBeInitialized" class="org.springframework.beans.testfixture.beans.MustBeInitialized"/>

	<bean id="lifecycle" class="org.springframework.beans.testfixture.beans.LifecycleBean"
		  init-method="declaredInitMethod">
		<property name="initMethodDeclared"><value>true</value></property>
	</bean>

	<bean id="protectedLifecycle" class="org.springframework.beans.factory.xml.ProtectedLifecycleBean"
		  init-method="declaredInitMethod">
		<property name="initMethodDeclared"><value>true</value></property>
	</bean>

	<!-- Factory beans are automatically treated differently -->
	<bean id="singletonFactory"	class="org.springframework.beans.testfixture.beans.factory.DummyFactory">
	</bean>

	<bean id="prototypeFactory"	class="org.springframework.beans.testfixture.beans.factory.DummyFactory">
		<property name="singleton"><value>false</value></property>
	</bean>

	<!-- Check that the circular reference resolution mechanism doesn't break
	     repeated references to the same FactoryBean -->
	<bean id="factoryReferencer" class="org.springframework.beans.factory.xml.DummyReferencer">
		<property name="testBean1"><ref bean="singletonFactory"/></property>
		<property name="testBean2"><ref bean="singletonFactory"/></property>
		<property name="dummyFactory"><ref bean="&amp;singletonFactory"/></property>
	</bean>

	<bean id="factoryReferencerWithConstructor" class="org.springframework.beans.factory.xml.DummyReferencer">
		<constructor-arg><ref bean="&amp;singletonFactory"/></constructor-arg>
		<property name="testBean1"><ref bean="singletonFactory"/></property>
		<property name="testBean2"><ref bean="singletonFactory"/></property>
	</bean>

	<!-- Check that the circular reference resolution mechanism doesn't break
	     prototype instantiation -->
	<bean id="prototypeReferencer" class="org.springframework.beans.factory.xml.DummyReferencer" scope="prototype">
		<property name="testBean1"><ref bean="kathy"/></property>
		<property name="testBean2"><ref bean="kathy"/></property>
	</bean>

	<bean id="listenerVeto" class="org.springframework.beans.testfixture.beans.TestBean">
		<property name="name"><value>listenerVeto</value></property>
		<property name="age"><value>66</value></property>
	</bean>

	<bean id="validEmpty" class="org.springframework.beans.testfixture.beans.TestBean"/>

	<bean id="commentsInValue" class="org.springframework.beans.testfixture.beans.TestBean">
	  <property name="name"><value>this is<!-- don't mind me --> a <![CDATA[<!--comment-->]]></value></property>
	</bean>

</beans>
	@Test
	public void withFreshInputStream() {
        // TODO 1.创建 SimpleBeanDefinitionRegistry 对象,提供注册BeanDefinition功能
		SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
        // TODO 2.获取配置文件
		Resource resource = new ClassPathResource("test.xml", getClass());
        // TODO 3.创建 XmlBeanDefinitionReader 对象,提供读取 XMl 文件中的 Bean 的定义信息;
		new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
        // TODO 30.验证 BeanDefinitions 信息;
		testBeanDefinitions(registry);
		System.out.println("success");
	}

	private void testBeanDefinitions(BeanDefinitionRegistry registry) {
		assertThat(registry.getBeanDefinitionCount()).isEqualTo(24);
		assertThat(registry.getBeanDefinitionNames().length).isEqualTo(24);
		assertThat(Arrays.asList(registry.getBeanDefinitionNames()).contains("rod")).isTrue();
		assertThat(Arrays.asList(registry.getBeanDefinitionNames()).contains("aliased")).isTrue();
		assertThat(registry.containsBeanDefinition("rod")).isTrue();
		assertThat(registry.containsBeanDefinition("aliased")).isTrue();
		assertThat(registry.getBeanDefinition("rod").getBeanClassName()).isEqualTo(TestBean.class.getName());
		assertThat(registry.getBeanDefinition("aliased").getBeanClassName()).isEqualTo(TestBean.class.getName());
		assertThat(registry.isAlias("youralias")).isTrue();
		String[] aliases = registry.getAliases("aliased");
		assertThat(aliases.length).isEqualTo(2);
		assertThat(ObjectUtils.containsElement(aliases, "myalias")).isTrue();
		assertThat(ObjectUtils.containsElement(aliases, "youralias")).isTrue();
	}
	/**
	 * Load bean definitions from the specified XML file.
	 * @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
	 *
	 * TODO XmlBeanDefinitionReader加载资源的入口方法
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		// TODO 4.将读取的XML资源进行特殊的编码处理
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	/**
	 * 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
	 *
	 * TODO 这里载入XML形式Bean配置信息方法
	 */
	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.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		// TODO 5. 将资源文件转为InputStream的I/O流
		try (InputStream inputStream = encodedResource.getResource().getInputStream()) { // TODO 只有是Closeable的子类才能这样书写,有点是系统会自动帮助我们关闭
			// TODO 从InputStream中得到XML的解析源
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			// TODO 6.具体读取过程
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
            // TODO 29. 
			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
	 *
	 *
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			// TODO 7. 加载Bean配置信息,并转换为文档对象
			Document doc = doLoadDocument(inputSource, resource);
			// TODO 8. 对Bean定义进行解析
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		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);
		}
	}
	
	/**
	 * 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
	 *
	 * TODO 按照Spring的Bean语义要求将Bean配置信息解析并转换为内部数据结构
	 *
	 */
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		// TODO 9.得到BeanDefinitionDoucumentReader来对XML格式的BeanDefinition进行解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		// TODO 10.获得容器中注册的Bean数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		// TODO 11. 解析过程的入口,这里使用了委派模式,BeanDefinitionDocumentReader只是一个接口
		// TODO 具体的解析过程由实现类DefaultBeanDefinitionDocumentReader完成
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		// TODO 28. 统计解析的Bean数量
		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.
	 *
	 * TODO 根据Spring DTD对Bean的定义规则解析Bean定义的文档对象
	 */
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		// TODO 获得XML描述符
		this.readerContext = readerContext;
		// TODO 12. doc.getDocumentElement() 获的Document根元素
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
/**
	 * 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.
		// TODO 具体的解析过程由BeanDefinitionParserDelegate实现
		// TODO BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各元素
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		// TODO 校验是否引入 beans schemaLocation
		if (this.delegate.isDefaultNamespace(root)) {

			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;
				}
			}
		}
		// TODO 在解析Bean定义之前,进行自定义解析,增强解析过程的可扩展性
		preProcessXml(root);
		// TODO 12.从文档的根元素开始进行Bean定义的文档对象的解析
		parseBeanDefinitions(root, this.delegate);
		// TODO 27.在解析Bean定义之后,进行自定义解析,增强解析过程的可扩展性
		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
	 *
	 *	TODO 使用Spring的Bean规则从文档的根元素开始Bean定义的文档对象的解析
	 */
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// TODO 13. Bean定义的文档对象使用了Spring默认的XML命名空间
		if (delegate.isDefaultNamespace(root)) {
			// TODO 14. 获取Bean定义的文档对象根元素的所有子节点
			NodeList nl = root.getChildNodes();
            // TODO 此处是循环,忽略需要,需要是大体执行流程不是唯一
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				// TODO 15. 获得的文档节点是XML元素节点
				if (node instanceof Element ele) {
					// TODO 16. Bean定义的文档的元素节点使用的是Spring默认的XML命名空间
					if (delegate.isDefaultNamespace(ele)) {
						// TODO 17. 使用Spring的Bean规则解析元素节点
						parseDefaultElement(ele, delegate);
					}
					else {
						// TODO 如果没有使用Spring默认的XMl命名空间,则使用用户自定义的规则解析元素节点
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			// TODO 如果没有使用Spring默认的XMl命名空间,则使用用户自定义的规则解析元素节点
			delegate.parseCustomElement(root);
		}
	}
	/**
	 * TODO 使用Spring的Bean规则解析文档元素节点
	 * @param ele
	 * @param delegate
	 */
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// TODO 18.如果节点是<import>导入元素,进行导入元素解析
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		// TODO 19.如果节点是<alias>别名元素,进行别名解析
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		// TODO 20.如果节点是<bean>元素,则按照Spring的Bean规则解析元素
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		// TODO 如果节点是<beans>元素,则按照Spring的Bean规则解析元素
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
	/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 * TODO		解析Bean资源文档对象的普通元素
	 * TODO     示例:<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		// TODO 21. BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
		// TODO 对文档对象中<bean>元素的解析由BeanDefinitionParserDelegate实现
		// TODO BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 22.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.
			// TODO 26.在完成向Spring Ioc容器注册解析得到的Bean定义之后,发送注册事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
	/**
	 * Register the given bean definition with the given bean factory.
	 * @param definitionHolder the bean definition including name and aliases
	 * @param registry the bean factory to register with
	 * @throws BeanDefinitionStoreException if registration failed
	 *
	 * TODO 将解析的BeanDefinitionHole注册到Spring Ioc容器
	 */
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {
		// Register bean definition under primary name.
		// TODO 获取解析的beanDefinition的名称
		String beanName = definitionHolder.getBeanName();
		// TODO 23.向Spring Ioc容器注册BeanDefinition
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		// TODO 25.如果解析的BeanDefinition有别名,向Spring Ioc容器注册别名
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
	/**
	 * TODO 注册 BeanDefinition 信息
	 */
    @Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "'beanName' must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        // TODO 24.存储 BeanDefinition 
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值