首页 > 其他分享 >4、xml配置文件解析之[默认]命名空间[标签]的解析

4、xml配置文件解析之[默认]命名空间[标签]的解析

时间:2023-04-08 11:59:26浏览次数:36  
标签:xml bd 配置文件 标签 beanName ele bean 解析

全局目录.md

引子

1、容器最基本使用.md

系列1 - bean 标签解析:

2、XmlBeanFactory 的类图介绍.md

3、XmlBeanFactory 对xml文件读取.md

4、xml配置文件解析之【默认】命名空间【标签】的解析.md

5、xml配置文件解析之【自定义】命名空间【标签】的解析.md

系列2 - bean 获取: getBean() 做了什么

前言

一句话概括:

  • 读取 xml 文件中硬编码的各种标签,解析为spring 容器可以识别的形式,并注册到:BeanFactory 中 (BeanDefinitionRegistry)

【只读取配置内容,并注册管理】

1 前文回顾

书接上回,上文终止余如下图所示的位置:

2 bean.xml 实例

既然要真刀真枪的将 xml 文件的标签解析了,那么我们不能继续无实物表演了,下边的例子是从spring 5.x 源码里随机找打的一个 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 default-lazy-init="true" default-autowire="constructor" default-merge="true"
       default-init-method="myInit" default-destroy-method="myDestroy">

    <import resource="beanEventsImported.xml"/>

    <alias name="testBean" alias="testBeanAlias1"/>

    <alias name="testBean" alias="testBeanAlias2"/>

    <bean id="testBean" class="org.springframework.tests.sample.beans.TestBean">
        <constructor-arg type="java.lang.String" value="Rob Harrop"/>
        <property name="friends">
            <ref bean="testBean2"/>
        </property>
        <property name="doctor">
            <bean class="org.springframework.tests.sample.beans.NestedTestBean">
                <constructor-arg type="java.lang.String" value="ACME"/>
            </bean>
        </property>
    </bean>

    <bean id="testBean2" class="org.springframework.tests.sample.beans.TestBean">
        <property name="name" value="Juergen Hoeller"/>
        <property name="spouse">
            <bean class="org.springframework.tests.sample.beans.TestBean">
                <property name="name" value="Eva Schallmeiner"/>
            </bean>
        </property>
    </bean>

</beans>

上述作为案例的:beanEvents.xml 文件中包含了,我们在 "默认命名空间" 下所关注的四种标签都有,用来做本文的案例简直再完美不过了。

先来个简单的介绍:

  • beans

    • 一个beans 包含0到多个 bean、0到多个 import、0到多个 alias,所以对beans 的解析会最终变成对如下几个标签的递归解析
  • alias

    • bean的别名定义,这个应该是这几个标签最简单的了
  • bean

    • 这个标签是本文绝对的主角,也是spring系列里的核心角色
    • bean 标签,拥有但是不局限于下图所示的直接属性
      • 所谓直接属性就是 这种可直接配置的
      • 就算不认识全部,总有眼熟的吧?平时写的注解是不是有出现在下图呢? 至于用途就不再赘述了

  • import

    • 从给出的xml文件案例里也能看出,import就是导入了一个外部定义的 "bean.xml"。

      对它的解析会变成对这个外部引入的 "bean.xml" 的递归解析。

3 spring 默认命名空间解析:parseDefaultElement

接下来进入 parseDefaultElement 方法内部,见下图:

方法代码的4个分支,代表的就是 4 中默认命名空间标签的解析,这里重点关注的只有最重要的: bean 标签。

4 关于 bean 标签的解析 processBeanDefinition

这里用红色裱起来了,就说明又到了:关键的代码简单,事情不简单环节了。

  • 4.1 标记的第一行:

    • 委托 BeanDefinitionDelegate 类对象,解析 bean 标签的上的属性。

      返回值类型:BeanDefinitionHolder 顾名思义,可以把它看作一个 bean 标签配置的相关属性的容器,
      至于是哪些属性,后续展开。

  • 4.2 标记第二行:

    • 当 bean 标签下边还存在:非spring 官方的支持的,用户自己魔改标签时,需要经过第二行代码,再进行了一轮自定义标签的解析。【不是本文重点关注的内容】
    • 如下所示, bean 标签下又有了 mybean 标签,这个一看就不是spring 官方的原装货,所以它需要被单独解析,自定义标签的解析动作被委托给了:
      • delegate.decorateBeanDefinitionIfRequired()
<bean id="test" class="test.xxx.TestXxx">
  <mybean:user userage="22" />
</bean>
  • 4.3 标记第三行

    • xml 配置解析结束,解析结果需要注册到:XmlBeanFactory
  • 4.4 标记第四行

    • 代表解析动作结束了,这里触发一个响应事件,告知相关的监听器,这个 bean 已经加载成功了。

本章前边提到的四行代码会对应后续的: 【第5 ~ 第8 章】

如果后边忘记了,可以回过头来看看。

5 bean 标签默认属性的解析

本节对应的是 4.1 所标注的那一行代码

这里实际上还没进入正题:

  • 简单的处理下id、name、别名
  • 重点是 标注出来的那一行代码
    • 上图中,标注的这行代码之后的内容也挺简单的,就是判断 beanName 若为空将按照一定规则自动生成,就像你用了 @Bean @Service @Compement 等等注解,但是没有指定 name那样,容器会自动生成 beanName

下边说回正题:

  • 看下图中标记的三块代码,它们代表着 [第五章] 的3个小节

5.1 上图标注的第一部分:解析结果的承载者的初始化

上图中,依稀可见这么一行代码:
AbstractBeanDefinition bd = createBeanDefinition(className, parent);

这里创建的 BeanDefinition 对象,就是用来承载,我们bean的配置内容解析结果的。

就像你配置的 xml对象,你自己能直接读懂,但是要让 spring 容器去读它时,spring容器会将这个 xml 文本内容进行翻译, 而bd [BeanDefinition] 承载的就是翻译结果。

顺着createBeanDefinition() 方法进去,最终会发现 bd的类型固定是: GenericBeanDefinition,下边是它的类图:

我们还记得 XmlBeanFactory 有个重要的接口:

  • BeanDefinitionRegistry ,它管理的就是:BeanDefinition。
  • 实际上,BeanDefinitionRegistry 就是以map 的形式,对spring 的配置信息进行保存的。

PS 而到目前为止,我们的所有篇幅都在介绍 这个翻译过程, 且还没介绍完。

至于为什么敢说它承载了 xml的翻译结果,请看下图所示的 AbstractBeanDefinition 类的所有成员变量,请问是不是很眼熟呢?

拓展讲点东西,AbstractBeanDefinition 实际上有三个子类:

已知 xml 中可以定义: 父/子 bean的关系

  • RootBeanDefinition:早期版本中,若 bean 没有父bean 则用 RootBeanDefinition
  • ChildBeanDefinition: 早期版本中用于,若 bean 定义了父bean 则用 ChildBeanDefinition 承载 bean 配置属性
  • GenericBeanDefinition:他是spring 2.5 版本后提供的一站式 BeanDefinition 服务类
    • 我们从 当前的spring 5.x 版本的源码中对这个问题,就可以窥见一些东西。
      你看上述的 createBeanDefinition() 方法,内部 bd 的实例化就一个选择:GenericBeanDefinition。

瞅瞅:父子bean的例子

5.2 标注的第二部分:bean标签硬编码的 [直接]属性解析

被标注的代码

// 硬编码解析默认的bean属性    所有元素 "属性" 解析
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

详情:

        /**
	 * Apply the attributes of the given bean element to the given bean * definition.
	 * @param ele bean declaration element
	 * @param beanName bean name
	 * @param containingBean containing bean definition
	 * @return a bean definition initialized according to the bean element attributes
	 */
	public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
		// 是否单例
		if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
			error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
		}
		// 作用范围 public class ? 
		else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
			bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
		}
		else if (containingBean != null) {
			// Take default from containing bean in case of an inner bean definition.
			bd.setScope(containingBean.getScope());
		}
		// 是否抽象abstract 
		if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
			bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
		}
		// 懒加载 属性  延迟加载
		String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
		if (isDefaultValue(lazyInit)) {
			lazyInit = this.defaults.getLazyInit();
		}
		bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
		// 是否 autowrie 自动装配
		String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
		bd.setAutowireMode(getAutowireMode(autowire));
		// depends-on 依赖 属性  依赖检查  spring 3.0 以后弃用
		if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
			String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
			bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
		}
		// 自动装配条件、前提  属性   值为 false 时,该bean不会被作为其他bean自动自动装配的候选者,
		// 但是它自身自动装配时,可以使用别的bean作为它自己的候选者
		String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
		if (isDefaultValue(autowireCandidate)) {
			String candidatePattern = this.defaults.getAutowireCandidates();
			if (candidatePattern != null) {
				String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
				bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
			}
		}
		else {
			bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
		}
		// primary 属性  (初级、初始) 
		if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
			bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
		}
		// 初始化方法  属性。。。
		if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
			String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
			bd.setInitMethodName(initMethodName);
		}
		else if (this.defaults.getInitMethod() != null) {
			bd.setInitMethodName(this.defaults.getInitMethod());
			bd.setEnforceInitMethod(false);
		}
		// 注销、关闭方法 属性
		if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
			String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
			bd.setDestroyMethodName(destroyMethodName);
		}
		else if (this.defaults.getDestroyMethod() != null) {
			bd.setDestroyMethodName(this.defaults.getDestroyMethod());
			bd.setEnforceDestroyMethod(false);
		}
		if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {  // factory-method 属性
			bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
		}
		if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {  // factory-bean 属性
			bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
		}
		return bd;
	}

5.3 标注的第三块代码:bean标签 - 子标签属性解析

我们重新把那张图请回来:

看原谅色的注解

5.3.1 子标签属性用法介绍

  • 1)、 meta:

    元数据, 看图中案例,就是些键值对形式配置的数据;
    元数据并非 bean 配置的 class 的属性,它是一个额外的声明;
    需要使用时,通过 BeanDefinition 的 getAttribute(key) 方法获取。

  • 2)、 lookup-method:

    • 这个属性用得实际是使用比较少,看下边的案例:

      • id="lookUpTest" 的bean配置的 class是抽象类:LookUpTest.java(理论上来说抽象类是不能被实例化的)。

        但是按下边的方法配置后,你会发现:

        当你通过容器调用如下代码:并不会报错:

        LookUpTest obj = (LookUpTest) new XmlBeanFactory("lookUpTest.xml").getBean("lookUpTest")

        obj.execute();

        这里execute() 内部的抽象方法 getUser() 会获取到xml 中配置的id="userBean"的 java.User对象

    • 如果你了解设计模式,就知道这里的妙用了

      • 如果是策略模式:我们可以通过xml配置文件,动态决定注入何种算法。
  • 3)、 replaced-method:

    • 上一节提到的 lookup-method 动态替换抽象方法返回的bean;

    • 而replaced-method 动态替换 已经实现的方法 逻辑;

    • 看看上图的案例:
      它会把 executeBean 的 doSomething() 方法的逻辑替换为:doSomethingReplacer中重写的方法逻辑

    • 但是它有个要求:被用来替换的,doSomethingReplacer所属的类必须实现如下接口

    • 最终 doSomething 方法的逻辑将会被替换为:reimplement() 方法所执行的逻辑
  • 4)、 constructor:

    • 如上图所示,顾名思义,其作用就是将另一个bean配置为当前bean构造函数的入参
  • property:

    图一出,就不用介绍干啥用的了吧?

  • qualifier:

    至于它,就更简单了,可以认为它是,我们在给bean注入属性的时候,指定过滤条件

    • 【spring 要求通过同样的beanName,只能匹配到一个满足条件的bean,否则容器将会报错】。
    • 回忆下,我们通过注解使用 @Qualifier,其实就是在设置过滤,spring 需要保证,最终满足 qualifier 条件条件的bean只能有一个:
      @Qualifier("integerRepo")
      private Repository<?> integerRepositoryQualifierProvider;
      

    qualifier,甚至还支持通过属性进行过滤:

    • 假如我们将多个 UserBean 注入了容器,qualifier 支持通过 UserBean的属性进行过滤

    如下所示 的是 qualifier 的三种配置方式:

<beans>
  <!-- 只通过BeanName=foo222 过滤 -->
  <bean id="foo" class="java.lang.String">
    <qualifier value="foo222" />
  </bean>

  <!-- Bean名称 + Bean类型过滤,常见于多态场景下 -->
  <bean class="org.springframework.beans.factory.xml.QualifierAnnotationTests$Person">
    <qualifier type="QualifierAnnotationTests.SimpleValueQualifier" value="curly"/>
  </bean>

  <!-- Bean类型 + Bean属性过滤 UserBean.name="moe" && UserBean.age="15"   -->
  <bean class="org.springframework.beans.factory.xml.QualifierAnnotationTests$Person">
    <property name="name" value="Moe Jr."/>
    <qualifier type="QualifierAnnotationTests.MultipleAttributeQualifier">
      <attribute key="name" value="moe"/>
      <attribute key="age" value="15"/>
    </qualifier>
  </bean>
</beans>

下图是 qualifier 的一般使用场景:

  • dataSource 和 dataSource2 明显是同类型的不同bean
  • 假如 location 配置 sql不同时,它们就不能随意混用了
  • 所以可以通过 beanName 不同这一点进行过滤

5.3.2 子标签属性解析源码

上边说完了他们的用法,下边我们说说他们是怎么被,spring 从xml配置文件中识别出来的:

  • meta:

    如图所示,就是通过 key - value 获取 meta标签配置的键值对,

    依赖 [key, value] 生成了 BeanMetadataAttribute 实例,最后注入了BeanDefinition中

<?xml version="1.0" encoding="UTF-8"?>
<bean class="org.xxx.xml.QualifierAnnotationTests$Person">
      <meta key="name" value="moe"/>
      <meta key="age" value="42"/>
</bean>
  • lookup-method:

    解析的代码跟meta的解析大同小异。

    解析的结束动作,可以视为:就是简单的拿到 lookup-method 标签注入的:抽象方法名称、bean名称
    然后通过,MethodOverrides 属性间接注入的 BeanDefinition 中

  • replaced-method:

  • constructor:

    构造函数参数解析,逻辑稍微长了一丢丢,这里主要是因为,构造函数参数可以设置顺序。

    如果设置了顺序[index],那么需要校验 数字是否合规,数字是否重复等等问题,最终将解析的结果注入到了 BeanDefinition 中。

  • property:

    跟上边的解析过程大同小异

  • qualifier:

到此,bean 标签下的,默认标签、元素的解析完成了

6 默认标签中的自定义标签元素解析

下图所示的是:第四章所述的第二段代码,bean 标签下的自定义标签的解析。

下一篇文章将细讲,这里只做简单的介绍。

  • 遍历读取标签

浅浅的说一下下图的行为:

  • 1 根据标签识别其所属的命名空间

  • 2 跳过 spring 默认命名空间下的标签

  • 3 根据命名空间获取,该非默认命名空间标签的,处理器 【NamespaceHandler】
    你或许会好奇,怎么突然蹦出个:NamespaceHandler啊,前文并没有任何地方提到它啊?
    实际上,NamespaceHandler,由自定义标签的人提供,如果我们自定义了自己的标签
    那么我们需要在 spring 解析配置前,去容器中注入我们自己开发的:NamespaceHandler

  • 4 解析非默认命名空间标签 【下一篇文章细说,非默认命名空间 - 标签,的解析】

7 BeanDefinition 的注册

将前文解析到的 BeanDefinition 注册到,容器中。

经过前文介绍可知, XmlBeanFactory 继承了一个接口,没错,就是下图所述的接口:

  • BeanDefinitionRegistry

这个接口负责对 BeanDefinition 信息的管理。

继续跟踪上图中的

  • registerBeanDefinition() 方法,来到此行的终点,
  • 已知 BeanDefinitionReaderUtils.registerBeanDefinition() 的参数:
    BeanDefinitionRegistry registry是个接口,那么上哪去找它的实现类呢?当然是:
    XmlBeanFactory 的 类图了:这下该知道方法实现在哪了吧?

下边代码中最核心的一句代码:

this.beanDefinitionMap.put(beanName, beanDefinition);

这就是说:BeanDefinitionRegistry 通过 Map 以键值对形式管理beanDefinition 的直接证据。

    // 通过 beanName 注册/记录 
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		// 空校验
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		// 是否是AbstractBeanDefinition 子类  
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				//  注册前最后一次校验,不同于xml校验
				//  它是对 类定义的(AbstractBeanDefinition)  methodOverrides属性的校验
				//  校验其是否于工厂方法并存、   是否存在(??)
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		//  检查是否已经注册
		if (existingDefinition != null) {
			//  已注册
			if (!isAllowBeanDefinitionOverriding()) {//  不允许覆盖,抛出异常
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + existingDefinition + "] bound.");
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {//   bean 的应用(??范围??) 变化
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isWarnEnabled()) {
					logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {//  关键信息变化,不能视作同一个bean ?? 
				if (logger.isInfoEnabled()) {
					logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			//  类定义BeanDefinition 维护到线程安全的 Map 中
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			//  还未注册过
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);  //  维护map 
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
		else if (isConfigurationFrozen()) {
			clearByTypeCache();
		}
	}

8 触发响应事件

再看事件监听器,没错它是从 XmlBeanDefinitionReader 中注入的,你看世界又闭环了。

【PS * 到了这里,你可能已经忘了 XmlBeanDefinitionReader是谁了,不急我来给你重新介绍下:】

你看下图的setter 方法,这里明显是支持注入自定义监听器的,没有自定义则直接注入默认的监听器。

9 结语

9.1 前文总结

到此,本文终于结束了,spring 默认命名空间下的标签已经解析结束了。

至此,BeanDefinitionRegistry 中已经注册好了 BeanDefinition 的信息了;后续,在 getBean() 的流程中, BeanDefinitionRegistry 中注册的 BeanDefinition 将再次粉墨登场。

<如果你不关注:第三方定义的命名空间怎么解析;你可以跳过下一篇文章,直接看getBean 到底干了啥了>

回顾下,本文提到了spring 的4个标签,我们用了绝大部分的篇幅在讲, bean 标签及其子标签、属性的解析,是因为另外的三个标签不重要吗?

  • 是的,它们不重要。而且它们的解析过程,要么极其简单,要么就是对 bean 标签解析的套娃[递归] 应用,所以这里讲 bean 标签的解析就已经达到目的了。
  • 如果,你看完了bean 标签解析,还看不懂另外三个标签的解析,那么你需要注意,你是否进度拉太快了,之前的内容你消化了么?

————————

beans: 它的实质就是递归调用 bean 标签的解析过程

alias: 为bean注册别名,方便同一个bean可以通过不同的beanName 来引用,代码也简单就不展开了

bean: 老熟人了,不解释

import: 下图就是 import 标签的解析,看看标注的那行代码?是不是一眼顶针? 小黑子在每个地方都会非常的显眼。

  • getReaderContext().getReader() 获取的不就是 当前 XmlBeanFactory 上的 XmlBeanDefinitionReader 么?
    所以,世界又重启了呀,我们又回答了苹果摊前

至于 XmlBeanFactory 不需要介绍了吧?再问紫纱。

————————

9.2 整点正常的活:

如下截图里从左到右的几个类,也是XmlBeanFactory 解析的大致流程。

这里看第三个类的名字? 直译过来,这不是我们一直反复念叨的:

  • 默认标签解析么

  • 既然有了默认 标签解析类,那会不会存在一个:自定义标签解析器类呢? 它会不会也跟 DefaultBeanDefinitionDocumentReader 一样,实现了: BeanDefinitionDocumentReader 接口呢?

  • 亦或者自定义标签的解析 也借助: DefaultBeanDefinitionDocumentReader 来完成,但是解析,为了解析自定义标签,我们会不会对: DefaultBeanDefinitionDocumentReader 上,做点别的配置呢?

带着上述两个猜想,我们进入下一章节的旅行,在该章节中,我们将亲自解开上述问题的答案。

标签:xml,bd,配置文件,标签,beanName,ele,bean,解析
From: https://www.cnblogs.com/bokers/p/17294704.html

相关文章

  • 3、XmlBeanFactory 对xml文件读取
    全局目录.md引子1、容器最基本使用.md系列1-bean标签解析:2、XmlBeanFactory的类图介绍.md3、XmlBeanFactory对xml文件读取.md4、xml配置文件解析之【默认】命名空间【标签】的解析.md5、xml配置文件解析之【自定义】命名空间【标签】的解析.md系列2-bean获取:get......
  • spring——bean初始化过程解析
         ......
  • C#判断字符串是否是有效的XML格式数据
    说明在try-catch语句块中,创建XmlDocument对象,并使用LoadXml方法加载xml字符串。如果没有异常,则说明xml字符串是有效的,返回true,反之为false。代码实现///<summary>///Xml字符串格式验证///</summary>///<paramname="xmlString">Xm......
  • 软考高项第4版教程-差异点解析来啦(第18章)!
    继续高项信息系统项目管理师第4版教程的差异点解析,今天带来第18章的解析。估计还会有朋友问,上次解析到了第5章,这次怎么直接跳到了第18章呢?中间的章节不解析了吗?其实上次已经预报过了,这次在开头再解释下,咱们这次解析的重点在“差异点”3个字,我先把差异变化比较大的章节解析完成,再回......
  • flask源码解析
    flask源码解析本篇主要是针对于以下一些问题进行源码剖析,并补充解释一些python语法的用法与应用场景。flask生命周期流程flask的request、session等都是导入进来的,也就意味着每次请求,我们所用的都是同一个request对象,它为什么能够按照同种方式取到自己request对象值呢flask......
  • [博客入坑]CS231N assignment 1 _ KNN 知识 & 详细解析
    从零开始的全新博客我之前一直在语雀上更新文章,但是一会不更发现居然需要VIP才能发博客了:不过考虑到自己确实有一会没写博客了,之前对神经网络在课上学过,也鼓捣过pytorch,但是深感自己没有系统学习过.第一次接触这种公开课,希望也能有种从零开始的感觉,让自己面对这......
  • 分布式存储技术(下):宽表存储与全文搜索引擎的架构原理、特性、优缺点解析
    对于写密集型应用,每天写入量巨大,数据增长量无法预估,且对性能和可靠性要求非常高,普通关系型数据库无法满足其需求。对于全文搜索和数据分析这类对查询性能要求极高的场景也是如此。为了进一步满足上面两类场景的需求,有了宽表存储和搜索引擎技术,本文将对他们的架构、原理、优缺点做介......
  • 分布式存储技术(下):宽表存储与全文搜索引擎的架构原理、特性、优缺点解析
    对于写密集型应用,每天写入量巨大,数据增长量无法预估,且对性能和可靠性要求非常高,普通关系型数据库无法满足其需求。对于全文搜索和数据分析这类对查询性能要求极高的场景也是如此。为了进一步满足上面两类场景的需求,有了宽表存储和搜索引擎技术,本文将对他们的架构、原理、优缺点做介......
  • Go-context源码解析
    首先我们简单的来看一个例子,如下:(学好这个例子,我们就可以说完全掌握住context了,并且能重构一个contextfuncmain(){ ctx,cancel:=context.WithCancel(context.Background()) ctxV:=context.WithValue(ctx,1,"HelloWorld") gofunc(ctxcontext.Context){ val:=......
  • SDL_AudioSpec 解析以及使用说明
    前言SDL_AudioSpec是包含音频输出格式的结构体,同时它也包含当音频设备需要更多数据时调用的回调函数。解析头文件说明typedefstructSDL_AudioSpec{intfreq;/**<DSPfrequency--samplespersecond*/SDL_AudioFormatformat;/**<Audiod......