Spring XML默认标签配置解析注册Bean源码分析(五)——BeanDefinitionParserDelegate

3.3.3. BeanDefinition解析与注册之BeanDefinitionParserDelegate

解析注册交给BeanDefinitionDocumentReader之后,由doRegisterBeanDefinitions方法做后续流程处理。

一般do开头的方法都是实际的操作方法。

// 源码位置:org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
/**
 * 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)) {
		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;
}

第一步就是创建了BeanDefinitionParserDelegate对象。这一节我们就以BeanDefinitionParserDelegate为主角,看BeanDefinition的解析过程是如何进行的。

3.3.3.1. 创建BeanDefinitionParserDelegate

BeanDefinitionParserDelegate从类名看是一个“委托类”。
我们看一下这个类的内容:

public static final String TRUE_VALUE = "true";
public static final String FALSE_VALUE = "false";
public static final String DEFAULT_VALUE = "default";
public static final String DESCRIPTION_ELEMENT = "description";
public static final String AUTOWIRE_NO_VALUE = "no";
public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
public static final String NAME_ATTRIBUTE = "name";
public static final String BEAN_ELEMENT = "bean";
public static final String META_ELEMENT = "meta";
public static final String ID_ATTRIBUTE = "id";
public static final String PARENT_ATTRIBUTE = "parent";
public static final String CLASS_ATTRIBUTE = "class";
public static final String ABSTRACT_ATTRIBUTE = "abstract";
public static final String SCOPE_ATTRIBUTE = "scope";
private static final String SINGLETON_ATTRIBUTE = "singleton";
public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
public static final String AUTOWIRE_ATTRIBUTE = "autowire";
public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
public static final String PRIMARY_ATTRIBUTE = "primary";
public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
public static final String INDEX_ATTRIBUTE = "index";
public static final String TYPE_ATTRIBUTE = "type";
public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
public static final String KEY_TYPE_ATTRIBUTE = "key-type";
public static final String PROPERTY_ELEMENT = "property";
public static final String REF_ATTRIBUTE = "ref";
public static final String VALUE_ATTRIBUTE = "value";
public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
public static final String REPLACER_ATTRIBUTE = "replacer";
public static final String ARG_TYPE_ELEMENT = "arg-type";
public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
public static final String REF_ELEMENT = "ref";
public static final String IDREF_ELEMENT = "idref";
public static final String BEAN_REF_ATTRIBUTE = "bean";
public static final String PARENT_REF_ATTRIBUTE = "parent";
public static final String VALUE_ELEMENT = "value";
public static final String NULL_ELEMENT = "null";
public static final String ARRAY_ELEMENT = "array";
public static final String LIST_ELEMENT = "list";
public static final String SET_ELEMENT = "set";
public static final String MAP_ELEMENT = "map";
public static final String ENTRY_ELEMENT = "entry";
public static final String KEY_ELEMENT = "key";
public static final String KEY_ATTRIBUTE = "key";
public static final String KEY_REF_ATTRIBUTE = "key-ref";
public static final String VALUE_REF_ATTRIBUTE = "value-ref";
public static final String PROPS_ELEMENT = "props";
public static final String PROP_ELEMENT = "prop";
public static final String MERGE_ATTRIBUTE = "merge";
public static final String QUALIFIER_ELEMENT = "qualifier";
public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";

这一部分定义了一堆静态变量,看一下这些变量的值会发现都是xml里面的一些属相配置项。

private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();

创建了一个DocumentDefaultsDefinition对象。

@Nullable
private String lazyInit;

@Nullable
private String merge;

@Nullable
private String autowire;

@Nullable
private String autowireCandidates;

@Nullable
private String initMethod;

@Nullable
private String destroyMethod;

@Nullable
private Object source;

// getter/setter... ...

这是一个JavaBean,用于保存Spring的XML配置文档里面<beans>标签上的默认属相值,比如default-lazy-init, default-autowire等等。

/**
 * 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 {@code parentDefaults}
 * in case the defaults are not explicitly set locally.
 * @param defaults the defaults to populate
 * @param parentDefaults the parent BeanDefinitionParserDelegate (if any) defaults to fall back to
 * @param root the root element of the current bean definition document (or nested beans element)
 */
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
	String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
	if (isDefaultValue(lazyInit)) {
		// Potentially inherited from outer <beans> sections, otherwise falling back to false.
		lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
	}
	defaults.setLazyInit(lazyInit);

	String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
	if (isDefaultValue(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 (isDefaultValue(autowire)) {
		// Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
		autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
	}
	defaults.setAutowire(autowire);

	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());
	}

	defaults.setSource(this.readerContext.extractSource(root));
}

这个方法用于向DocumentDefaultsDefinition对象set默认值。

再后面是一些以parseXXX开头的方法名,用于解析XML的各个部分。

因此BeanDefinitionParserDelegate类实际是提供了对Spring配置文件的解析功能。对于默认命名空间下的元素的解析工作在该类中实现,对于其他命名空间下的元素通过NamespaceHandler接口的各实现类完成,我们在自定义标签的解析与注册部分详细说明。

在doRegisterBeanDefinitions方法中this.delegate = createDelegate(getReaderContext(), root, parent);创建BeanDefinitionParserDelegate对象,在创建该对象的过程中调用populateDefaults方法对<beans>标签的元素值进行填充默认值并set到DocumentDefaultsDefinition对象中。

3.3.3.2. profile属性

先大概看一下profile属性的用法,下面是官方提供的一段XML配置片段:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="development">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

通过profile属相,我们定义了一个开发环境一个生产环境,这样我们就可以根据不同的环境加载相对应的<beans标签,比如例子中的切换数据源,当然需要用到哪个profile配置,需要进行profile激活,这里我们不做过多的解释,只了解它的作用。

在BeanDefinitionParserDelegate对象创建之后,我们看到下一步Spring对profile属性进行验证,首先判断是否设置了profile项,如果定义了则到环境变量getEnvironment()中去寻找,最后判断其profile的激活状态。如果在环境变量中未找到该配置或处于未激活状态,则终止解析流程。

3.3.3.3. preProcessXml & postProcessXml

preProcessXml和postProcessXml方法,用于在处理BeanDefinition之前和之后的扩展处理。在DefaultBeanDefinitionDocumentReader类中不做什么事情。

protected void preProcessXml(Element root) {
}

protected void postProcessXml(Element root) {
}
3.3.3.4. parseBeanDefinitions

我们用了很长的篇幅跟踪Spring解析XML以生成BeanDefinition的过程,到这里到了时候我们看到了希望,因为我们即将到达解析的核心流程,在parseBeanDefinitions方法中,Spring通过判断是否是默认命名空间,决定最终的解析流程。

// 源码位置:org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	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);
	}
}

我们知道Spring的XML配置还有默认标签配置和自定义标签配置。在这个方法中XML的解析流程走上了分叉路口。默认标签配置走parseDefaultElement(ele, delegate);方法,自定义标签走delegate.parseCustomElement(root);方法。因为我们的文章主线是以分析默认标签配置为主,因此这里我们重点跟踪parseDefaultElement(ele, delegate);方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值