xml解析之默认标签解析

接上篇通过xml文件加载BeanDefinition (opens new window)

默认标签解析方法封装在DefaultBeanDefinitionDocumentReader#parseDefaultElement

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        //解析import标签
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        //解析alias标签
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        //解析bean标签
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// 嵌套 递归调用 DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions
		doRegisterBeanDefinitions(ele);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 解析import

<import resource="classpath:applicationContext-dao-config.xml" />
1
	protected void importBeanDefinitionResource(Element ele) {
        //获取配置文件地址
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// Resolve system properties: e.g. "${user.dir}"
        //占位符替换
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

		Set<Resource> actualResources = new LinkedHashSet<>(4);

		// Discover whether the location is an absolute or relative URI
		boolean absoluteLocation = false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			// cannot convert to an URI, considering the location relative
			// unless it is the well-known Spring prefix "classpath*:"
		}

		// Absolute or relative?
		if (absoluteLocation) {
			try {
                //解析导入的资源文件 还是使用XmlBeanDefinitionReader.loadBeanDefinitions来完成
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
			}
            //...
		}
		else {
			// No URL -> considering resource location as relative to the current file.
            //相对地址
			try {
				int importCount;
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					String baseLocation = getReaderContext().getResource().getURL().toString();
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
			}
			//..
		}
		Resource[] actResArray = actualResources.toArray(new Resource[0]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

总结下来就是获取到资源文件的地址,然后调用XmlBeanDefinitionReader.loadBeanDefinitions对导入的资源文件进行解析

解析完导入文件后,spring还会发布一个消息,监听器是XmlBeanDefinitionReader.eventListener

private ReaderEventListener eventListener = new EmptyReaderEventListener();
1

默认是一个空实现,会丢弃消息不处理,也可以自定义实现ReaderEventListener,如CollectingReaderEventListener

从上文可知XmlBeanDefinitionReader的创建是在AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory)中完成,

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// Configure the bean definition reader with this context's
	// resource loading environment.
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// Allow a subclass to provide custom initialization of the reader,
	// then proceed with actually loading the bean definitions.
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如果使用自定义的监听器,则需手动设置,并主动刷新容器。

//使用GenericApplicationContext是因为它在刷新容器时不会再解析配置文件,
GenericApplicationContext context = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
//CollectingReaderEventListener位于testfixture包中,此处只是举例
reader.setEventListener(new CollectingReaderEventListener());
reader.loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
1
2
3
4
5
6
7

# 解析alias

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
1
protected void processAliasRegistration(Element ele) {
	String name = ele.getAttribute(NAME_ATTRIBUTE);
	String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
	boolean valid = true;
	//...
	if (valid) {
		try {
            //获取BeanDefinitionRegistry,记录别名
			getReaderContext().getRegistry().registerAlias(name, alias);
		}
		//...
        //发布解析别名消息
		getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 解析bean

xml格式,具体可查看Spring官方文档 (opens new window)

<bean id="clientService"  class="examples.ClientService" factory-method="createInstance"/>

<bean id="beanOne" class="x.y.ThingOne">
    <constructor-arg ref="beanTwo"/>
    <constructor-arg ref="beanThree"/>
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="misterkaoli"/>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //解析Bean并包装到BeanDefinitionHolder中
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
        //解析bean标签中的自定义标签
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// 注册到bean定义中心
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// 发布事件消息
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    //id
    String id = ele.getAttribute(ID_ATTRIBUTE);
    //name 别名
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

	List<String> aliases = new ArrayList<>();
	if (StringUtils.hasLength(nameAttr)) {
        //别名可以多个,以,或;分隔
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}

	String beanName = id;
	if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
		beanName = aliases.remove(0);
		if (logger.isTraceEnabled()) {
			logger.trace("No XML 'id' specified - using '" + beanName +
					"' as bean name and " + aliases + " as aliases");
		}
	}

	if (containingBean == null) {
        //检查不能重复
		checkNameUniqueness(beanName, aliases, ele);
	}
    //生成BeanDefinition
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {
            //没有指定beanName也没有指定别名,生成一个beanName
			try {
				if (containingBean != null) {
					beanName = BeanDefinitionReaderUtils.generateBeanName(
							beanDefinition, this.readerContext.getRegistry(), true);
				}
				else {
					beanName = this.readerContext.generateBeanName(beanDefinition);
					// Register an alias for the plain bean class name, if still possible,
					// if the generator returned the class name plus a suffix.
					// This is expected for Spring 1.2/2.0 backwards compatibility.
					String beanClassName = beanDefinition.getBeanClassName();
					if (beanClassName != null &&
							beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
							!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
						aliases.add(beanClassName);
					}
				}
			}
			catch (Exception ex) {
				error(ex.getMessage(), ele);
				return null;
			}
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}

	return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, @Nullable BeanDefinition containingBean) {

	this.parseState.push(new BeanEntry(beanName));

	String className = null;
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();//解析class属性
	}
	String parent = null;
	if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
		parent = ele.getAttribute(PARENT_ATTRIBUTE);//解析parent属性
	}

	try {
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);//创建BeanDefinition

		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//解析bean属性
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
		parseMetaElements(ele, bd);//解析元数据
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//解析lookup-method
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//解析replaced-method

		parseConstructorArgElements(ele, bd);//解析构造方法
		parsePropertyElements(ele, bd);//解析property子元素
		parseQualifierElements(ele, bd);//解析qualifier子元素

		bd.setResource(this.readerContext.getResource());//
		bd.setSource(extractSource(ele));

		return bd;
	}
    //...

	return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

lookup methodj介绍 (opens new window)
replace method介绍 (opens new window)

回到processBeanDefinition方法,初步创建BeanDefinition,之后还会处理bean标签中的自定义标签,这部分的内容放在xml解析之自定义标签解析 (opens new window)

上次更新: 2023/04/09, 16:34:32
最近更新
01
go-admin-ui项目仿写练手1-登录页
06-29
02
maven依赖问题
06-17
03
JVM相关命令
02-21
更多文章>