Spring——2. 默认标签的解析

默认标签的解析

接着上文Spring——1. BeanFactory容器的初始化,在Spring中,XML配置里面有两大类型的Bean声明:默认类型Bean 和 自定义类型Bean;对于不同的类型解析的方式差别很大,这里先进行默认类型标签的解析。

DefaultBeanDefinitionDocumentReader.java

parseDefaultElement(ele, delegate);
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		// import标签的解析;
		// 当项目比较庞大的时候,需要进行分模块,可以使用import来导入其他模块的配置文件
		// <import resource="dependency-lookup-context.xml"/>
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		// alias标签的解析;
		// 在定义bean时就指定所有的别名并不总是恰当的,有时候期望能在当前位置为在别处定义的bean引入别名;
		// <bean id="user" class="org...."/>
		// <alias name="user" alias="user-bgy1,user-bgy2"/>
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		// bean标签的解析和注册
		// <bean id="" class="" />
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// 嵌入式 beans标签的解析;递归调用beans的解析过程
		// <beans>
		//     <bean></bean>
		// </beans>
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

1. bean标签的解析及注册

这四种标签的解析中,bean标签的解析是最为核心的地方,也是最复杂的,所以先从bean标签的解析开始。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 委托BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 方法进行元素解析,返回BeanDefinitionHolder 类型的实例;
	// BeanDefinitionHolder:Holder for a BeanDefinition with name and aliases.
	// bdHolder实例已经包含了配置文件中配置的各种属性了(class、name、id、alias)
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		// 如果需要的话,就对BeanDefinition进行装饰
		/**
		 * 适用场景:
		 * 当spring中的bean使用的是默认的标签配置,但是其中的子元素是自定义配置(子元素,不是以bean的形式存在,是一个属性)
		 *
		 * <bean id="test" class="test.MyClass">
		 *     <mybean:user username="aaa"/>
		 * </bean>
		 */
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		// 解析、装饰 完成后,对于得到的BeanDefinition已经可以满足后续的使用要求了,只需要进行注册了;
		// 对解析后的 BeanDefinition 进行注册
		try {
			// 注册操作委托给了 BeanDefinitionReaderUtils
			// 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);
		}
		// 发出响应事件,通知相关的监听器,这个bean的解析和注册 已经完成了
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

代码上的注释基本都解释了每个步骤的内容,下面来具体看看各个步骤的实现。

1.1 解析BeanDefinition

BeanDefinitionParserDelegate.java

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
	return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
	// 获取id
	String id = ele.getAttribute(ID_ATTRIBUTE);
	// 获取name
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	// 分割name属性(可能有多个name),设置别名
	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);
	}
	// 解析标签其他属性,并封装到 GenericBeanDefinition
	/**
     * 在这里是完成,从 XML文档 到 GenericBeanDefinition 的转换,也就是说
     * XML中的所有配置,都可以在 GenericBeanDefinition 的实例中找到对应的配置属性
     */
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {
			// 如果beanName不存在,就根据spring中提供的命名规则为当前的bean生成对应的beanName
			try {
				if (containingBean != null) {
					beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
				} else {
					// 生成beanName
					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);
					}
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Neither XML 'id' nor 'name' specified - " +
							"using generated bean name [" + beanName + "]");
				}
			} catch (Exception ex) {
				error(ex.getMessage(), ele);
				return null;
			}
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		// 将获取到的信息封装到 BeanDefinitionHolder 实例中
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}
	return null;

这个方法中完成了以下工作:

  1. 获取到元素的id以及name属性,并且检查beanName是否已经被使用了;
  2. 解析元素的其他所有属性,封装到 GenericBeanDefinition的实例中;即把XML配置文件转换成Java对象的过程;
  3. 检测到如果这个bean没有指定beanName,就使用默认规则为这个bean生成一个beanName;
    • 将这些解析到的结果封装到 BeanDefinitionHolder中;

进一步查看步骤2中,对XML配置文件其他属性的解析过程:

public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, @Nullable BeanDefinition containingBean) {
	this.parseState.push(new BeanEntry(beanName));
	String className = null;
	// 解析class属性
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}
	String parent = null;
	// 解析parent属性
	if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
		parent = ele.getAttribute(PARENT_ATTRIBUTE);
	}
	try {
		// 创建用于承载属性的 AbstractBeanDefinition类型的 GenericBeanDefinition(<bean>元素标签在容器中的内部表示形式)
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// 硬编码解析默认bean的各种属性
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		// 提取description
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
		/**
		 * <bean id="user" class="org.springframework.beans.example.bean.User">
		 *     <meta key="testStr" value="aaa"/>
		 * </bean>
		 *
		 */
		// 解析子元素meta
		parseMetaElements(ele, bd);
		// 解析子元素 lookup-method 属性
		// 获取器注入:获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里配置的;
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
		// 解析子元素 replaced-method属性
		// 方法替换:可以在运行时使用新的方法替换现有的方法(不但可以动态地替换返回实体bean,还能动态地更改原有方法的逻辑)
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// 解析子元素 constructor-arg
		/**
		 * <bean id="testBean" class="org.TestBean>
		 *     <constructor-arg>
		 *         <value>a</value>
		 *     </constructor-arg>
		 * </bean>
		 */
		parseConstructorArgElements(ele, bd);
		// 解析 Property子元素
		/**
		 * <bean id="testBean" class="org.TestBean>
		 *     <property name="testStr" value="aaa"/>
		 * </bean>
		 *
		 * 或
		 *
		 * <bean id="testBean" class="org.TestBean>
		 *     <property name="testList">
		 *         <list>
		 *             <value>aa</value>
		 *             <value>bb</value>
		 *         </list>
		 *     </property>
		 * </bean>
		 *
		 * property中必须含有name属性
		 */
		parsePropertyElements(ele, bd);
		// 解析 Qualifier子元素
		/**
		 * spring允许通过Qualifier指定注入bean的名称
		 *
		 * <bean id="myTestBean" class="bean.MyTestBean">
		 *     <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf"/>
		 * </bean>
		 *
		 * Qualifier中必须含有type属性
		 */
		parseQualifierElements(ele, bd);
		bd.setResource(this.readerContext.getResource());
		bd.setSource(extractSource(ele));
		return bd;
	} catch (ClassNotFoundException ex) {
		error("Bean class [" + className + "] not found", ele, ex);
	} catch (NoClassDefFoundError err) {
		error("Class that bean class [" + className + "] depends on not found", ele, err);
	} catch (Throwable ex) {
		error("Unexpected failure during bean definition parsing", ele, ex);
	} finally {
		this.parseState.pop();
	}
	return null;
}

bean标签中的所有属性都在这里被解析完成,下面继续看一些复杂标签的属性解析:

1.1.1 创建用于承载属性的 BeanDefinition

BeanDefinition是一个接口,抽象类 AbstractBeanDefinition实现了这个接口;AbstractBeanDefinition是配置文件 元素标签在容器中的内部表现形式。元素标签中有 class、scope、lazy-init等属性,AbstractBeanDefinition中则提供了相应的 beanClass、scope、lazyInit属性,AbstractBeanDefinition和中的属性是一一对应的。

1.1.1.1 AbstractBeanDefinition

AbstractBeanDefinition.java

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
	// bean的作用范围,对应bean属性 scope
	@Nullable
	private String scope = SCOPE_DEFAULT;

	// 是否是抽象,对应bean属性 abstract
	private boolean abstractFlag = false;

	// 是否延迟加载,对应bean属性 lazy-init
	@Nullable
	private Boolean lazyInit;

	// 自动注入模式,对应bean属性 autowire
	private int autowireMode = AUTOWIRE_NO;

	// 依赖检查,3.0 后弃用此属性
	private int dependencyCheck = DEPENDENCY_CHECK_NONE;

	// 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性 depend-on
	@Nullable
	private String[] dependsOn;

	// autowire-candidate 属性设置为false,容器在查找自动装配对象时,将不考虑该bean;
	// 即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他 bean
	// 对应bean属性 autowire-candidate
	private boolean autowireCandidate = true;

	// 自动装配时出现多个bean后选择,将作为首选者,对应bean属性 primary
	private boolean primary = false;

	// 用于记录Qualifier,对应bean属性 子元素qualifiers
	private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();

	@Nullable
	private Supplier<?> instanceSupplier;

	// 允许访问非公开的构造器和方法,程序设置
	private boolean nonPublicAccessAllowed = true;

	// 是否以一种宽松的模式解析构造函数,默认为true
	private boolean lenientConstructorResolution = true;

	// 对应bean属性 factory-bean
	/**
	 * 用法:
	 * <bean id="instanceFactoryBean" class="example.chapter3.InstanceFactoryBean"/>
	 * <bean id="currentTime" factory-bean="instanceFactoryBean" factory-method="createTime"/>
	 */
	@Nullable
	private String factoryBeanName;

	// 对应bean属性 factory-method
	@Nullable
	private String factoryMethodName;

	// 记录构造函数注入属性,对应bean属性 子元素 constructor-arg
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;

	// 记录普通属性集合,对应bean属性 子元素 property
	@Nullable
	private MutablePropertyValues propertyValues;

	// 方法重写的持有者,记录lookup-method、replaced-method元素,对应bean属性 子元素 lookup-method、replaced-method
	private MethodOverrides methodOverrides = new MethodOverrides();

	// 初始化方法,对应bean属性 init-method
	@Nullable
	private String initMethodName;

	// 销毁方法,对应bean属性 destroy-method
	@Nullable
	private String destroyMethodName;

	// 是否执行 init-method,程序设定
	private boolean enforceInitMethod = true;

	// 是否执行 destroy-method,程序设定
	private boolean enforceDestroyMethod = true;

	// 是否是用户定义的而不是应用程序本身定义的,创建AOP的时候为true,程序设置
	private boolean synthetic = false;

	private int role = BeanDefinition.ROLE_APPLICATION;

	// bean的描述信息
	@Nullable
	private String description;

	// 这个bean定义的资源
	@Nullable
	private Resource resource;
	
	//省略其他方法
}

抽象类AbstractBeanDefinition有三种具体实现:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。其中RootBeanDefinition是常用的实现类,对应一般性的元素标签;GenericBeanDefinition是2.5版本后新加入的实现类,是一站式服务类,基本实现了所有属性;在XML配置文件中可以定义父和子,父用RootBeanDefinition表示,子用ChildBeanDefinition表示;

1.1.1.2 GenericBeanDefinition

GenericBeanDefinition.java

public class GenericBeanDefinition extends AbstractBeanDefinition {

	@Nullable
	private String parentName;
	// 忽略其他方法
}

Spring通过 BeanDefinition将配置文件中的配置信息转换成了容器的内部表示形式。因此,要解析属性首先就要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
		throws ClassNotFoundException {
	return BeanDefinitionReaderUtils.createBeanDefinition(
			parentName, className, this.readerContext.getBeanClassLoader());
}

BeanDefinitionReaderUtils.java

public static AbstractBeanDefinition createBeanDefinition(
		@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
	GenericBeanDefinition bd = new GenericBeanDefinition();
	bd.setParentName(parentName);
	if (className != null) {
		if (classLoader != null) {
			// 如果classLoader不为空,就使用传入的classLoader同一虚拟机加载类对象,否则只是记录 className
			bd.setBeanClass(ClassUtils.forName(className, classLoader));
		}
		else {
			bd.setBeanClassName(className);
		}
	}
	return bd;
}

在这里如果传入的classLoader不为空,就直接使用这个classLoader根据解析出来的className通过反射创建对应的Class对象;如果为空,就只是设置className。

1.1.2 解析各种属性

在创建了bean信息的承载实例后,就可以进行bean信息的各种属性的解析了:

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);
	}
	// 解析scope属性
	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.
		// 在嵌入 BeanDefinition情况下,且没有单独制定scope属性,则使用父类默认的属性
		bd.setScope(containingBean.getScope());
	}
	// 解析 abstract属性
	if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
		bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
	}
	// 解析 lazy-init属性
	String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
	if (isDefaultValue(lazyInit)) {
		lazyInit = this.defaults.getLazyInit();
	}
	// 如果没有设置lazy-init属性,或者被设置为其他字符,都会被设置为false
	bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
	// 解析 autowire属性
	String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
	// 设置自动装配的模式
	bd.setAutowireMode(getAutowireMode(autowire));
	// 解析 dependency-on 属性
	if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
		String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
		bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
	}
	// 解析 autowire-candidate 属性
	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)));
	}
	// 解析 init-method 属性
	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);
	}
	// 解析 destroy-method 属性
	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);
	}
	// 解析 factory-method属性
	if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
		bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
	}
	// 解析 factory-bean属性
	if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
		bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
	}
	return bd;
}

在这个方法里面,Spring对bean的所有属性进行一一的解析,解析之后设置到标签的属性承载实例 GenericBeanDefinition中。

1.1.3 解析子元素 meta

parseMetaElements(ele, bd);

元数据meta的使用:

<bean id="user" class="org.springframework.beans.example.bean.User">
    <meta key="testStr" value="aaa"/>
</bean>
标签并不会体现在User的属性中,而是一个额外的声明,当需要使用里面的信息的时候,可以通过 BeanDefinition的getAttribute(key)方法进行获取。

回到代码:

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
	NodeList nl = ele.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
			Element metaElement = (Element)node;
			// 提取meta
			String key = metaElement.getAttribute(KEY_ATTRIBUTE);
			String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
			// 使用key value构造BeanMetadataAttribute
			BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
			attribute.setSource(extractSource(metaElement));
			attributeAccessor.addMetadataAttribute(attribute);
		}
	}
}

1.1.4 解析子元素 lookup-method

parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

子元素 lookup-method不是很常用,但是在某些时候也是非常有用的属性,通常称它为 获取器注入

获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean;但实际上要返回的具体bean是在xml配置文件里配置的,这个方法就可以用在设计有些可插拔的功能上

使用示例:

  1. 首先创建一个父类:
public class User {
	public void showMe() {
		System.out.println("i am user.");
	}
}
  1. 创建一个子类并覆盖父类的showMe()方法:
public class Teacher extends User {
	@Override
	public void showMe() {
		System.out.println("i am teacher.");
	}
}
  1. 创建一个调用类,使用获取器注入:
public abstract class LookupTest {
	// 返回一个User类型的bean,但可以有User的不同实现类
	public abstract User getUser();

	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:lookup-method-context.xml");
		LookupTest lookupTest = (LookupTest)applicationContext.getBean("lookupTest");
		User user = lookupTest.getUser();
		user.showMe();
	}
}

到这里整个测试方法已经完成了,但是会有一个疑问:这个抽象类的抽象方法都没有被实现,怎么可以直接调用呢?

原因就是需要使用Spring提供的获取器注入,xml配置文件为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="lookupTest" class="org.bgy.spring.study.spring.bean.definition.lookup.method.LookupTest">
        <lookup-method name="getUser" bean="teacher"/>
    </bean>

    <bean id="teacher" class="org.bgy.spring.study.spring.bean.definition.lookup.method.Teacher"/>

</beans>

在这个配置文件中,使用了 lookup-method 子元素,这个配置的功能就是动态地将teacher的bean,作为getUser()方法的返回值,实现了把bean注入到LookupTest中
此时控制台会打印:i am teacher.

如果业务变更了,我们需要有不同的User来实现不同的showMe()的逻辑的时候,我们可以增加新的逻辑类:

public class Student extends User {
	@Override
	public void showMe() {
		System.out.println("i am student.");
	}
}

同时修改配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="lookupTest" class="org.bgy.spring.study.spring.bean.definition.lookup.method.LookupTest">
        <lookup-method name="getUser" bean="student"/>
    </bean>

    <bean id="teacher" class="org.bgy.spring.study.spring.bean.definition.lookup.method.Teacher"/>

    <bean id="student" class="org.bgy.spring.study.spring.bean.definition.lookup.method.Student"/>
</beans>

此时控制台会打印:i am student.

回到代码:

public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 仅在spring默认bean的子元素下,且为 <lookup-method>时有效
		if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
			Element ele = (Element)node;
			// 获取要修饰的方法
			String methodName = ele.getAttribute(NAME_ATTRIBUTE);
			// 获取配置中要返回的 bean
			String beanRef = ele.getAttribute(BEAN_ELEMENT);
			// 构造一个 LookupOverride 属性(也是MethodOverride属性)
			LookupOverride override = new LookupOverride(methodName, beanRef);
			override.setSource(extractSource(ele));
			// 记录在了BeanDefinition的 methodOverride属性中
			overrides.addOverride(override);
		}
	}
}

1.1.5 解析子元素 replaced-method

parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

方法替换:Spring可以在运行时用新的方法替换现有的方法;不但可以动态地替换返回的bean类型,还能动态地更改原有方法的逻辑。

使用示例:

  1. 先创建一个带有changeMe()方法的测试类,并且增加main()方法进行测试:
public class ReplacedTest {
	public void changeMe() {
		System.out.println("change me.");
	}

	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:replaced-method-context.xml");
		ReplacedTest replacedTest = (ReplacedTest)applicationContext.getBean("replacedTest");
		replacedTest.changeMe();
	}
}
  1. 在需要改变changeMe()方法的逻辑的时候,创建一个替换类:
public class MethodReplacerTest implements MethodReplacer {
	@Override
	public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
		System.out.println("changed.");
		return null;
	}
}
  1. 在xml文件中进行配置,使替换类生效:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="replacedTest" class="org.bgy.spring.study.spring.bean.definition.replaced.method.ReplacedTest">
        <replaced-method name="changeMe" replacer="methodReplacerTest"/>
    </bean>

    <bean id="methodReplacerTest" class="org.bgy.spring.study.spring.bean.definition.replaced.method.MethodReplacerTest"/>
</beans>

此时控制台会打印:changed.

使用了标签,把replacedTest的bean的changeMe()方法的返回值和具体的实现逻辑,动态替换成了methodReplacerTest的bean中的重载方法。

回到代码:

public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 仅在spring默认bean的子元素下,且为 <replaced-method>时有效
		if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
			Element replacedMethodEle = (Element)node;
			// 获取要替换的旧的方法
			String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
			// 获取对应的新的替换方法
			String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
			// 构造一个 ReplaceOverride 属性(也是MethodOverride属性)
			ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
			// Look for arg-type match elements.
			List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
			for (Element argTypeEle : argTypeEles) {
				// 记录参数
				String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
				match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
				if (StringUtils.hasText(match)) {
					replaceOverride.addTypeIdentifier(match);
				}
			}
			replaceOverride.setSource(extractSource(replacedMethodEle));
			// 记录在了BeanDefinition的 methodOverride属性中
			overrides.addOverride(replaceOverride);
		}
	}

这里基本就只比上面的子元素 lookup-method 多了一个记录参数的步骤;最终都是把 methodOverride 记录在BeanDefinition中。

1.1.6 解析子元素 constructor-arg

parseConstructorArgElements(ele, bd);

xml中使用构造函数注入,应该是使用最多的方式之一,所以对构造函数的解析是非常常用的,但是也是非常复杂的。

使用示例:

  1. 首先创建一个有两个属性的bean类:
public class ConstructorArg {
	private String name;
	private String age;

	public ConstructorArg() {
	}

	public ConstructorArg(String name, String age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}
}
  1. 使用xml对这个bean进行构造器注入:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="constructorArg" class="org.bgy.spring.study.spring.bean.definition.constructor.arg.ConstructorArg">
        <!-- 通过index属性-->
        <constructor-arg index="0" value="bgy">
            <!-- <value>bgy</value> -->
        </constructor-arg>
        <!-- 通过name属性-->
        <constructor-arg name="age" value="25">
            <!-- <value>25</value> -->
        </constructor-arg>
    </bean>
</beans>

这样就可以实现,对constructorArg这个bean自动寻找对应这两个参数的构造函数,并在初始化的时候将设置的参数传入进去。

回到代码:

public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 遍历所有子元素,提取所有的 constructor-arg,进行解析
		if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
			// 解析 constructor-arg
			parseConstructorArgElement((Element)node, bd);
		}
	}
}

跟进:

public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
	// 提取index属性
	String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
	// 提取type属性
	String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
	// 提取name属性
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	// 如果配置了 index属性
	if (StringUtils.hasLength(indexAttr)) {
		try {
			int index = Integer.parseInt(indexAttr);
			if (index < 0) {
				error("'index' cannot be lower than 0", ele);
			} else {
				try {
					this.parseState.push(new ConstructorArgumentEntry(index));
					// 解析ele对应的属性元素
					Object value = parsePropertyValue(ele, bd, null);
					// 创建一个 ConstructorArgumentValues.ValueHolder 实例,用于封装解析出来的元素;
					ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
					// 并将type、name、index等属性一起封装到ConstructorArgumentValues.ValueHolder中
					if (StringUtils.hasLength(typeAttr)) {
						valueHolder.setType(typeAttr);
					}
					if (StringUtils.hasLength(nameAttr)) {
						valueHolder.setName(nameAttr);
					}
					valueHolder.setSource(extractSource(ele));
					if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
						error("Ambiguous constructor-arg entries for index " + index, ele);
					} else {
						// 添加到当前BeanDefinition的ConstructorArgumentValues的 indexedArgumentValues属性中
						bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
					}
				} finally {
					this.parseState.pop();
				}
			}
		} catch (NumberFormatException ex) {
			error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
		}
	// 如果没有index属性则忽略此属性,自动寻找
	} else {
		try {
			this.parseState.push(new ConstructorArgumentEntry());
			// 解析ele对应的属性元素
			Object value = parsePropertyValue(ele, bd, null);
			// 创建一个 ConstructorArgumentValues.ValueHolder 实例,用于封装解析出来的元素;
			ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
			// 并将type、name、index等属性一起封装到ConstructorArgumentValues.ValueHolder中
			if (StringUtils.hasLength(typeAttr)) {
				valueHolder.setType(typeAttr);
			}
			if (StringUtils.hasLength(nameAttr)) {
				valueHolder.setName(nameAttr);
			}
			valueHolder.setSource(extractSource(ele));
			// 添加到当前BeanDefinition的ConstructorArgumentValues的 genericArgumentValues属性中
			bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
		} finally {
			this.parseState.pop();
		}
	}
}

在这个方法中:

  1. 提取constructor-arg上必要的元素(index、type、name);
  2. 判断是否指定了index属性,如果指定了先判断index不小于0;
  3. 解析constructor-arg的子元素;
  4. 使用 ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素;将type、name、index等属性都set进ConstructorArgumentValues.ValueHolder中;
  5. 如果指定了index,就把index和valueHolder添加到当前的BeanDefinition的 ConstructorArgumentValues 的 indexedArgumentValues 属性中
  6. 如果没有指定index,就把ValueHolder添加到当前的 BeanDefinition的 ConstructorArgumentValues 的 genericArgumentValues 属性中

可以看到,对于是否指定index,Spring的处理流程是不同的,关键在于属性信息的保存位置是indexedArgumentValues 还是 genericArgumentValues。

进一步了解解析constructor-arg的子元素:

public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
	String elementName = (propertyName != null ?
			"<property> element for property '" + propertyName + "'" :
			"<constructor-arg> element");
	// Should only have one child element: ref, value, list, etc.
	// 一个属性只能对应一种类型:ref、value、list等
	NodeList nl = ele.getChildNodes();
	Element subElement = null;
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 如果是 description或者meta就不处理
		if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
				!nodeNameEquals(node, META_ELEMENT)) {
			// Child element is what we're looking for.
			if (subElement != null) {
				error(elementName + " must not contain more than one sub-element", ele);
			} else {
				subElement = (Element)node;
			}
		}
	}
	// 提取 <constructor-arg>标签中的 ref和value属性,以便于根据规则验证正确性;
	boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
	boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
	// 不能同时有 ref和value;
	// 或有ref或者value的时候,不能有子元素
	if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
		error(elementName +
				" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
	}
	// ref 属性的处理
	if (hasRefAttribute) {
		String refName = ele.getAttribute(REF_ATTRIBUTE);
		if (!StringUtils.hasText(refName)) {
			error(elementName + " contains empty 'ref' attribute", ele);
		}
		// 使用RuntimeBeanReference封装对应的 ref名称, 如 <constructor-arg ref="a">
		RuntimeBeanReference ref = new RuntimeBeanReference(refName);
		ref.setSource(extractSource(ele));
		return ref;
	} else if (hasValueAttribute) { // value属性的处理
		// 使用TypedStringValue 封装对应的 value, 如 <constructor-arg value="a">
		TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
		valueHolder.setSource(extractSource(ele));
		return valueHolder;
	} else if (subElement != null) {
		// 解析子元素
		/**
		 * <constructor-arg>
		 *     <map>
		 *         <entry key="key" value="value"/>
		 *     </map>
		 * </constructor-arg>
		 */
		return parsePropertySubElement(subElement, bd);
	} else {
		// Neither child element nor "ref" or "value" attribute found.
		// 既没有 ref有没有value,也没有子元素,会报错
		error(elementName + " must specify a ref or value", ele);
		return null;
	}
}

这个方法不止用于解析constructor-arg标签的元素,还可以用于解析其他的property元素;如果用于解析constructor-arg元素的时候,propertyName为null。

解析步骤:

  1. 一个constructor-arg标签对应于构造函数中的一个属性,一个属性只能有一种类型(ref、value、list)。
    先遍历constructor-arg下所有子元素,如果是description或者meta属性,直接跳过不处理;否则记录下子元素,如果有多个,抛出error错误。
  2. 提取constructor-arg标签上的ref和value属性,并且根据规则验证其正确性;规则:
    1. 同时既有ref属性又有value属性,错误;
    2. 存在ref属性或者value属性的时候,又有子元素,错误;
  3. 根据constructor-arg标签指定的属性情况,进行处理:
    1. 如果是指定了ref属性,使用 RuntimeBeanReference 封装ref对应的bean名称,如:,并且返回RuntimeBeanReference;
    2. 如果是指定了value属性,使用TypedStringValue封装value对应的具体指,如:,并且返回使用TypedStringValue封装value对应的具体指;
    3. 如果都没有指定,且subElement不为null,则是使用了子元素的方式进行指定,则需要解析子元素;
    4. 如果都不满足,抛出error错误;

继续跟进对于各种子元素的分类处理:

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
	return parsePropertySubElement(ele, bd, null);
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
	if (!isDefaultNamespace(ele)) {
		// 如果不是默认命名空间,调用解析自定义元素的方法进行解析;(会根据自定义的解析器进行解析)
		return parseNestedCustomElement(ele, bd);
	} else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
		// 如果子元素的bean,则又递归调用bean标签的解析过程;
		BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
		if (nestedBd != null) {
			nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
		}
		return nestedBd;
	} else if (nodeNameEquals(ele, REF_ELEMENT)) { // 对ref子元素的解析
		// A generic reference to any name of any bean.
		String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
		boolean toParent = false;
		if (!StringUtils.hasLength(refName)) {
			// A reference to the id of another bean in a parent context.
			refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
			toParent = true;
			if (!StringUtils.hasLength(refName)) {
				error("'bean' or 'parent' is required for <ref> element", ele);
				return null;
			}
		}
		if (!StringUtils.hasText(refName)) {
			error("<ref> element contains empty target attribute", ele);
			return null;
		}
		RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
		ref.setSource(extractSource(ele));
		return ref;
	} else if (nodeNameEquals(ele, IDREF_ELEMENT)) { // 对idref子元素的解析
		return parseIdRefElement(ele);
	} else if (nodeNameEquals(ele, VALUE_ELEMENT)) { // 对value子元素的解析
		return parseValueElement(ele, defaultValueType);
	} else if (nodeNameEquals(ele, NULL_ELEMENT)) { // 对null子元素的解析
		// It's a distinguished null value. Let's wrap it in a TypedStringValue
		// object in order to preserve the source location.
		TypedStringValue nullHolder = new TypedStringValue(null);
		nullHolder.setSource(extractSource(ele));
		return nullHolder;
	} else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { // 解析array子元素
		return parseArrayElement(ele, bd);
	} else if (nodeNameEquals(ele, LIST_ELEMENT)) { // 解析list子元素
		return parseListElement(ele, bd);
	} else if (nodeNameEquals(ele, SET_ELEMENT)) { // 解析set子元素
		return parseSetElement(ele, bd);
	} else if (nodeNameEquals(ele, MAP_ELEMENT)) { // 解析map子元素
		return parseMapElement(ele, bd);
	} else if (nodeNameEquals(ele, PROPS_ELEMENT)) { // 解析props子元素
		return parsePropsElement(ele);
	} else {
		error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
		return null;
	}
}

解析步骤:

  1. 首先判断是不是自定义子元素,如果是的话就调用parseNestedCustomElement()方法进行解析,这个方法里面会获取到自定义元素的自定义解析器NamespaceHandler(下一章会讲)进行解析;
  2. 如果子元素是bean,则又会递归调用bean标签的解析步骤;
  3. 剩下的就是对ref、value、list…等元素的解析;

总的来说,在这个方法中,实现了所有支持的子元素的解析处理,到这里构造函数的解析流程基本完成。

1.1.7 解析子元素 property

parsePropertyElements(ele, bd);

xml中使用setter方法注入,也是使用得最多的方式之一。
使用示例:

<bean id="people" class="org.bgy.spring.study.spring.bean.definition.property.People">
    <property name="name" value="bgy"/>
    <property name="age" value="25"/>
</bean>

现在来看一下对于setter方法注入的property子元素的解析:

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 遍历所有元素,解析property属性的元素
		if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
			parsePropertyElement((Element)node, bd);
		}
	}
}

跟进:

public void parsePropertyElement(Element ele, BeanDefinition bd) {
	// 获取配置元素中 name 的值
	String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
	if (!StringUtils.hasLength(propertyName)) {
		error("Tag 'property' must have a 'name' attribute", ele);
		return;
	}
	this.parseState.push(new PropertyEntry(propertyName));
	try {
		// 不允许多次对同一属性进行配置
		if (bd.getPropertyValues().contains(propertyName)) {
			error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
			return;
		}
		// 解析ele对应的属性元素
		Object val = parsePropertyValue(ele, bd, propertyName);
		// 创建一个 PropertyValue 实例,用于封装解析出来的元素
		PropertyValue pv = new PropertyValue(propertyName, val);
		// 可能存在 meta,解析meta
		parseMetaElements(ele, pv);
		pv.setSource(extractSource(ele));
		// 添加到当前BeanDefinition的MutablePropertyValues的 propertyValueList属性中
		bd.getPropertyValues().addPropertyValue(pv);
	} finally {
		this.parseState.pop();
	}
}

解析步骤:

  1. 这里先通过BeanDefinition的processedProperties属性判断当前property属性是否已经被解析过了,如果解析过了直接抛出error异常;
  2. 根据propertyName来解析property元素;
  3. 把解析出来的元素封装到PropertyValue中;
  4. 记录到过BeanDefinition的processedProperties属性中;

这里你会发现,解析元素的的时候,使用的是我们上面的parsePropertyValue方法,只是propertyName参数不是null了,而是property标签的propertyName;所以解析方式跟上面一样。

1.1.8 解析子元素 qualifier

parseQualifierElements(ele, bd);

qualifier属性的使用:

  • 在Spring中,进行自动注入的时候,Spring容器中匹配的bean的数量必须有且只有一个;如果按照类型匹配,可能出现两个相同类型的bean,这个时候Spring容器就会抛出 NoUniqueBeanDefinitionException的异常,并且指出期望的是单个匹配的bean。
    这个时候,就可以使用qualifier标签来手动指定匹配的bean:
    <bean id="myTestBean" class="bean.MyTestBean">
        <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf"/>
    </bean>
    
  • 不过实际中,我们接触更多的是使用 @Qualifier注解的形式,特别是使用@Autowired注解时;详细示例可见:@Qualifier 详细解析

回到代码:

public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 遍历所有元素,解析qualifier属性的元素
		if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
			parseQualifierElement((Element)node, bd);
		}
	}
}

跟进:

public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
	// 获取配置元素中 type的值
	String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
	if (!StringUtils.hasLength(typeName)) {
		error("Tag 'qualifier' must have a 'type' attribute", ele);
		return;
	}
	this.parseState.push(new QualifierEntry(typeName));
	try {
		AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
		qualifier.setSource(extractSource(ele));
		String value = ele.getAttribute(VALUE_ATTRIBUTE);
		if (StringUtils.hasLength(value)) {
			qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
		}
		NodeList nl = ele.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
				Element attributeEle = (Element)node;
				String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
				String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
				if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
					BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
					attribute.setSource(extractSource(attributeEle));
					qualifier.addMetadataAttribute(attribute);
				} else {
					error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
					return;
				}
			}
		}
		// 添加到当前BeanDefinition的 qualifiers属性中
		bd.addQualifier(qualifier);
	} finally {
		this.parseState.pop();
	}
}

这里主要就是创建一个AutowireCandidateQualifier类的实例qualifier,然后把解析到的各个属性封装到这个实例中,最终把这个类的实例添加到BeanDefinition的 qualifiers属性中。

接着,设置资源,返回BeanDefinition:

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

至此,我们完成了整个对XML文档到 GenericBeanDefinition的转换过程,也就是说,XML文档中的所有配置都可以在 GenericBeanDefinition的实例中找到对应的配置。

回到这里把BeanDefinition封装到BeanDefinitionHolder中,并返回BeanDefinitionHolder的实例bdHolder;返回之后,要做的是:如果需要的话,就对BeanDefinition进行装饰:

DefaultBeanDefinitionDocumentReader.java

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		// 如果需要的话,就对BeanDefinition进行装饰
		/**
		 * 适用场景:
		 * 当spring中的bean使用的是默认的标签配置,但是其中的子元素是自定义配置(子元素,不是以bean的形式存在,是一个属性)
		 *
		 * <bean id="test" class="test.MyClass">
		 *     <mybean:user username="aaa"/>
		 * </bean>
		 */
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		// 省略其他部分
    }
}

适用场景在源码注解中也已经写了:XML文件中的标签使用的是默认标签,但是其中的子元素使用了自定义的标签元素(这个自定义类型不是以Bean的形式出现的,而是以属性的形式出现的),这个时候就需要再对子元素的自定义标签进行解析。

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

bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

BeanDefinitionParserDelegate.java

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
	return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}

这里的第三个参数是父类BeanDefinition(作用为使用父类的scope属性),当对某个嵌套配置进行分析时,这里需要传递父类的BeanDefinition;这里解析的是顶层配置,所以传递null。

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
	BeanDefinitionHolder finalDefinition = originalDef;
	// Decorate based on custom attributes first.
	NamedNodeMap attributes = ele.getAttributes();
	// 遍历所有的属性,看看是否有适合于修饰的属性
	for (int i = 0; i < attributes.getLength(); i++) {
		Node node = attributes.item(i);
		finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
	}
	// Decorate based on custom nested elements.
	NodeList children = ele.getChildNodes();
	// 遍历所有的子元素,看看是否有适合修饰的子元素
	for (int i = 0; i < children.getLength(); i++) {
		Node node = children.item(i);
		if (node.getNodeType() == Node.ELEMENT_NODE) {
			finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
		}
	}
	return finalDefinition;
}

在上面的代码中,分别对这个自定义元素的所有属性 以及子节点进行了decorateIfRequired,继续跟进:

public BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
	// 获取自定义标签的命名空间
	String namespaceUri = getNamespaceURI(node);
	// 对于非默认标签进行修饰(对于默认标签的处理是直接跳过的,因为默认标签到这里已经被处理完了,这里只对自定义的标签或者bean的自定义属性处理)
	if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
		// 根据命名空间找到对应的处理器
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler != null) {
			// 进行修饰
			BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
			if (decorated != null) {
				return decorated;
			}
		} else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
		} else {
			// A custom namespace, not to be handled by Spring - maybe "xml:...".
			if (logger.isDebugEnabled()) {
				logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
			}
		}
	}
	return originalDef;
}

在这里首先获取自定义标签的命名空间,并且只处理非默认命名空间的标签(因为默认标签的解析到这里是已经被处理完了的);然后根据自定义标签的命名空间,找到对应的自定义解析器NamespaceHandler进行解析。(跟这里的解析一样)

1.3 注册解析到的BeanDefinition

来到这里,对于XML配置文件,解析也解析完了装饰也装饰完了,这个时候的BeanDefinition已经可以满足后续的使用要求了,现在只需要再对BeanDefinition进行注册:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 委托BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 方法进行元素解析,返回BeanDefinitionHolder 类型的实例;
	// BeanDefinitionHolder:Holder for a BeanDefinition with name and aliases.
	// bdHolder实例已经包含了配置文件中配置的各种属性了(class、name、id、alias)
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		// 如果需要的话,就对BeanDefinition进行装饰
		/**
		 * 适用场景:
		 * 当spring中的bean使用的是默认的标签配置,但是其中的子元素是自定义配置(子元素,不是以bean的形式存在,是一个属性)
		 *
		 * <bean id="test" class="test.MyClass">
		 *     <mybean:user username="aaa"/>
		 * </bean>
		 */
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		// 解析、装饰 完成后,对于得到的BeanDefinition已经可以满足后续的使用要求了,只需要进行注册了;
		// 对解析后的 BeanDefinition 进行注册
		try {
			// 注册操作委托给了 BeanDefinitionReaderUtils
			// 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);
		}
		// 发出响应事件,通知相关的监听器,这个bean的解析和注册 已经完成了
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

BeanDefinitionReaderUtils.java

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 
        throws BeanDefinitionStoreException {
        
	// 使用beanName做唯一注册标识
	// Register bean definition under primary name.
	String beanName = definitionHolder.getBeanName();
	// 使用beanName的方式 注册 BeanDefinition
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
	// Register aliases for bean name, if any.
	// 遍历注册所有的别名
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			// 通过别名的方式 注册BeanDefinition
			registry.registerAlias(beanName, alias);
		}
	}
}

在上面的代码中,把解析得到的BeanDefinition注册到了BeanDefinitionRegistry类型的实例registry中,而对于BeanDefinition的注册分为两个部分:通过beanName的方式注册 以及 通过别名的方式注册。

1.3.1 通过beanName注册BeanDefinition

void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;

DefaultListableBeanFactory.java

//---------------------------------------------------------------------
// Implementation of BeanDefinitionRegistry interface
//---------------------------------------------------------------------
@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");
	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			/**
			 * 注册前的最后一次校验,这个校验不同于之前的XML文件校验
			 * 主要是对于AbstractBeanDefinition 属性中的methodOverrides校验
			 * 校验methodOverrides 是否与工厂方法并存,或者methodOverrides对应的方法根本不存在
			 */
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	// 使用了 ConcurrentHashMap,线程安全
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	// 如果这个beanName已经注册了
	if (existingDefinition != null) {
		// 如果不允许被覆盖,直接抛出异常
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (logger.isInfoEnabled()) {
				logger.info("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						existingDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(existingDefinition)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		// 加入map缓存,注册BeanDefinition
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	// 这个beanName没有被注册
	else {
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			synchronized (this.beanDefinitionMap) {
				// 加入map缓存,注册BeanDefinition
				this.beanDefinitionMap.put(beanName, beanDefinition);
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				removeManualSingletonName(beanName);
			}
		}
		else {
			// Still in startup registration phase
			// 加入map缓存,注册BeanDefinition
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}
	if (existingDefinition != null || containsSingleton(beanName)) {
		// 重置beanName对应的缓存
		resetBeanDefinition(beanName);
	}
	else if (isConfigurationFrozen()) {
		clearByTypeCache();
	}
}

主要步骤:

  1. 最后再对BeanDefinition进行一次校验,这里校验的主要是对于 methodOverrides 属性的校验:
    • methodOverrides属性和工厂方法一起存在时,抛出异常;
    • 检查methodOverrides对应的方法不存在时,抛出异常;
  2. beanName已经注册的情况下,如果设置了不允许bean的覆盖,则抛出异常;否则直接覆盖并加入map缓存,即注册BeanDefinitionthis.beanDefinitionMap.put(beanName, beanDefinition);
  3. beanName没有被注册的情况下,使用同步锁进行加入map缓存,,即注册BeanDefinitionthis.beanDefinitionMap.put(beanName, beanDefinition);
  4. 清除解析之前的beanName对应的缓存;

1.3.2 通过别名注册BeanDefinition

void registerAlias(String name, String alias);

SimpleAliasRegistry.java

public void registerAlias(String name, String alias) {
	Assert.hasText(name, "'name' must not be empty");
	Assert.hasText(alias, "'alias' must not be empty");
	synchronized (this.aliasMap) {
		// 如果beanName与alias相同的话,不记录alias,并删除对应的alias
		if (alias.equals(name)) {
			this.aliasMap.remove(alias);
			if (logger.isDebugEnabled()) {
				logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
			}
		}
		else {
			String registeredName = this.aliasMap.get(alias);
			// 如果这个alias已经被注册了
			if (registeredName != null) {
				// 并且注册的beanName是当前要注册的beanName,直接返回,不需要重复注册
				if (registeredName.equals(name)) {
					// An existing alias - no need to re-register
					return;
				}
				// 并且注册的beanName不是当前的,也就是说被指向了别的beanName;
				// 并且还不允许覆盖的话,抛出异常
				if (!allowAliasOverriding()) {
					throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
							name + "': It is already registered for name '" + registeredName + "'.");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
							registeredName + "' with new target name '" + name + "'");
				}
			}
			// alias循环检查
			checkForAliasCircle(name, alias);
			// 加入map,注册alias
			this.aliasMap.put(alias, name);
			if (logger.isTraceEnabled()) {
				logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
			}
		}
	}
}

主要步骤:

  1. 如果beanName与alias相同的话则不需要处理,并且删除原有aliasMap缓存中的alias;
  2. 如果这个alias已经被注册过了,分为两种情况:
    • 注册的beanName就是当前的beanName,则不需要重复注册,直接返回;
    • 如果注册的beanName不是当前的beanName,再判断是否允许覆盖,如果不允许则抛出异常;
  3. alias的循环检查:若 A -> B 存在时,如果再出现 A -> C -> B 的情况,则抛出异常;
  4. 把alias和对应的beanName注册到aliasMap缓存中;

1. 4 解析及注册完成,通知监听器

getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

这里的实现只是为了扩展,目前Spring中没有对这个事件做任务逻辑的处理;当开发人员需要对注册BeanDefinition事件进行监听时,可以通过注册监听器的方式,并将处理逻辑写入监听器中。

2. 嵌入式beans标签的解析

回到开头,我们对配置文件的解析包括了:import标签、alias标签、bean标签、beans标签;通过上面的内容我们已经完成了对最复杂的bean标签的解析,接下就继续对beans标签的解析。

嵌入式beans标签的使用示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
    <bean></bean>
    <beans>
        <bean></bean>
    </beans>
</beans>

DefaultBeanDefinitionDocumentReader.java

doRegisterBeanDefinitions(ele);

protected void doRegisterBeanDefinitions(Element root) {
	// 专门处理解析
	// BeanDefinitionParserDelegate 用于解析 xml bean 的状态委托类
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);
	// 在这里如果是默认的命名空间,即:http://www.springframework.org/schema/beans
	if (this.delegate.isDefaultNamespace(root)) {
		// 处理profile属性(可以配置多套不同的开发环境,便于切换)
		// 获取bean节点是否定义了profile属性,
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			// profile可以同时指定多个
			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.
			// 判断每一个profile是否都符合环境变量中所定义的,如果不符合则不会浪费性能去解析
			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;
			}
		}
	}
	// 解析前处理,留给子类实现(空方法,面向继承设计的,如果继承自DefaultBeanDefinitionDocumentReader的类,
	// 需要在Bean解析前后做一些处理的话,就可以重写这两个方法)
	preProcessXml(root);
	// 正式解析 BeanDefinition
	parseBeanDefinitions(root, this.delegate);
	// 解析后处理,留给子类实现(空方法,面向继承设计的,如果继承自DefaultBeanDefinitionDocumentReader的类,
	// 需要在Bean解析前后做一些处理的话,就可以重写这两个方法)
	postProcessXml(root);
	this.delegate = parent;
}

可以看到在这里调用了上一篇文章Spring——1. BeanFactory容器的初始化2.2.1 解析BeanDefinitions 讲到的doRegisterBeanDefinitions()方法,其实就是递归调用注册解析BeanDefinitions的过程,等于把本篇文章的所有流程再走一遍。

3. import标签的解析

在比较庞大的Spring项目中,如果整个项目使用一个Spring的配置文件,那么编写和维护起来都会特别麻烦。
Spring提供了import的方法,可以使得用户对于项目中的进行分模块编写配置文件,然后使用import方法导入到 applicationContext.xml 配置文件中:

<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <import resource="customerContext.xml" />
    <import resource="manageContext.xml" />
    <import resource="systemContext.xml" />
</beans>

如果以后有新模块的加入,就可以简单的修改这个配置文件进行导入;这样简化了配置文件的维护,并且使得项目模块化,易于管理。

回到代码看看Spring是如何解析import标签的:

importBeanDefinitionResource(ele);

protected void importBeanDefinitionResource(Element ele) {
	// 获取resource属性所表示的路径
	String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
	if (!StringUtils.hasText(location)) {
		// 如果不存在resource,不做任何处理
		getReaderContext().error("Resource location must not be empty", ele);
		return;
	}
	// 解析路径中的系统属性,格式如 ${user.dir}
	// Resolve system properties: e.g. "${user.dir}"
	location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
	Set<Resource> actualResources = new LinkedHashSet<>(4);
	// 判断location是相对URI还是绝对URI
	// 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) {
		// 如果是绝对URI,则直接根据地址加载对应的配置文件
		try {
			int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
			if (logger.isTraceEnabled()) {
				logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
			}
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error(
					"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
		}
	}
	else {
		// No URL -> considering resource location as relative to the current file.
		try {
			int importCount;
			// Resource存在多个实现类,如 ClassPathResource、FileSystemResource等;
			// 而每个resource的createRelative方式实现都不一样,所以这里先使用子类的方法尝试解析
			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);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
			}
		}
		catch (IOException ex) {
			getReaderContext().error("Failed to resolve current resource location", ele, ex);
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error(
					"Failed to import bean definitions from relative location [" + location + "]", ele, ex);
		}
	}
	// 解析后进行监听器激活处理
	Resource[] actResArray = actualResources.toArray(new Resource[0]);
	getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

主要步骤:

  1. 获取resource属性中指定的资源路径location,如果没有指定则直接返回不做任何处理;
  2. 解析指定的路径中的系统属性,因为可以通过"${user.dir}"等方式直接设置路径值;
  3. 判断location是绝对路径还是相对路径;
  4. 如果是绝对路径,则递归调用XML文档的解析过程,进行另一个XML文档的解析;
  5. 如果是相对路径,则直接根据这个相对路径创建出一个相对资源relativeResource,并判断这个资源是否存在:
    • 如果存在,也是递归调用XML文档的解析过程;
    • 如果不存在,则拿到父XML文档的路径,并且跟相对路径拼接出来一个绝对路径,再递归调用XML文档的解析过程;
  6. 解析完成后,通知监听器;

这里的递归调用XML文档的解析过程,其实就是根据location获取到resource,然后调用上一篇文章中讲的 2. 加载BeanDefinitions 步骤进行解析:

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
	ResourceLoader resourceLoader = getResourceLoader();
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException(
				"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
	}
	if (resourceLoader instanceof ResourcePatternResolver) {
		// Resource pattern matching available.
		try {
			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
			int count = loadBeanDefinitions(resources);
			if (actualResources != null) {
				Collections.addAll(actualResources, resources);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
			}
			return count;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	else {
		// Can only load single resources by absolute URL.
		Resource resource = resourceLoader.getResource(location);
		int count = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
		}
		return count;
	}
}

4. alias标签的解析

在对bean进定义的时候,除了使用id属性来指定名称之外,还可以提供多个名称,Spring中使用alias标签来实现这个功能。alias标签中所有的名称都指向同一个bean,在某些情况下提供别名非常有用,比如让应用的每个组件都能更容易地对公用组件进行引用

使用方式:

  1. 直接使用bean标签中的name属性:
<bean id="user" name="user1,user2" class="org...."/>
  1. 单独使用alias标签来指定别名:
<bean id="user" class="org...."/>
<alias name="user" alias="user1,user2"/>

一个具体的使用示例:
模块A在XML配置文件中定义了一个名为 componentA的DataSource类型的bean;但模块B想在自己的XML文件中以componentB来引用此bean;模块C又要想在自己的XML文件中以componentC来引用此bean;这样的话就可以这样配置:

<bean id="componentA" class="org...."/>
<alias name="componentA" alias="componentB"/>
<alias name="componentA" alias="componentC"/>

这样一来,各个模块都可以通过唯一的名字来引用同一个数据源,而互不干扰,结构和使用都更清晰。

回到代码:

processAliasRegistration(ele);

protected void processAliasRegistration(Element ele) {
	// 获取name
	String name = ele.getAttribute(NAME_ATTRIBUTE);
	// 获取 alias
	String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
	boolean valid = true;
	// 必须要有name
	if (!StringUtils.hasText(name)) {
		getReaderContext().error("Name must not be empty", ele);
		valid = false;
	}
	// 必须要有alias
	if (!StringUtils.hasText(alias)) {
		getReaderContext().error("Alias must not be empty", ele);
		valid = false;
	}
	// name和alias都有的情况下,验证通过,valid为true
	if (valid) {
		try {
			// 注册alias,将alias与beanName组成一对注册到registry中
			getReaderContext().getRegistry().registerAlias(name, alias);
		}
		catch (Exception ex) {
			getReaderContext().error("Failed to register alias '" + alias +
					"' for bean with name '" + name + "'", ele, ex);
		}
		// 注册alias之后,通知监听器做相应的处理
		getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
	}
}

这里主要就是先对name和alias做验证,并且在name和alias都存在的情况下才去解析;解析的过程,就是上面bean中 通过别名注册BeanDefinition 的过程。

5. 整体流程思维导图

最后附上一个整体流程的思维导图:Spring容器初始化体系,本篇文章对应其中的 默认标签的解析 部分。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值