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);
方法。