默认标签的解析之 Bean 标签解析
前言
第二章讲过 Spring 中的标签包括默认标签和自定义标签两种,而两种标签的用法和解析存在着很大不同,本章跟大家一起来学习下默认标签的解析过程。
默认标签是在 DefaultBeanDefinitionDocumentReader#parseDefaultElement 函数中进行的,parseDefaultElement 函数代码如下:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
分别对 4 种不同的标签(import、alias、bean、beans)做了不同的处理。
首先我们来对 bean 标签的解析来进行分析,进入函数 processBeanDefinition(ele, delegate)
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 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);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
大致逻辑如下:
- 1、委托 BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 方法进行元素解析,返回 BeanDefinitionHolder 类的实例 bdHolder,经过这个方法后,bdHolder 已经包含配置文件的各个属性了,如 class、name、id、alias 等。
- 2、当返回的 bdHolder 不为 null 的情况下,若存在默认标签的子节点下有自定义属性,还需要对自定义标签进行解析
- 3、解析完成后,需要对解析后的 bdHolder 进行注册,注册操作委托给了 BeanDefinitionReaderUtils#registerBeanDefinition 方法
- 4、最后发出响应时间,通知相关监听器,到这里,这个 bean 就加在完成了。
它的时序图如下
一、解析 BeanDefinition
接下来我们针对各个操作做具体分析,先从元素解析和信息提取入手,即
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
具体代码如下:
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//解析 id 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析 name 属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//分割 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);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果不存在 beanName,那么根据 Spring 中提供的命名规则,生成当前 bean 对应的 beanName
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);
}
}
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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
以上代码逻辑思路总结如下:
- 1、提取元素中的 id 及 name 属性。
- 2、进一步解析其他所有属性,并统一封装至 GenericBeanDefinition 类的实例中。
- 3、如果检测到 bean 没有指定 beanName,那么使用默认规则,为此 Bean 生成 beanName。
- 4、将获取到的信息封装到 BeanDefinitionHolder 实例中。
进一步解析步骤 2,即方法 parseBeanDefinitionElement,具体代码如下:
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
//解析 class 属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
//解析 parent 属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建用于承载属性的抽象类 AbstractBeanDefinition 的实现类 GenericBeanDefinition 实例
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//硬编码解析默认 bean 的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析元数据
parseMetaElements(ele, bd);
//解析 lookup-method 属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析 replaced-method 属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造函数参数
parseConstructorArgElements(ele, bd);
//解析 property 子元素
parsePropertyElements(ele, bd);
//解析 qualifier 子元素
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、创建用于属性承载的 BeanDefinition
BeanDefinition 是一个接口,在 Spring 中有三种实现:RootBeanDefinition、ChildBeanDefinition 和 GenericBeanDefinition 。三种实现均继承了 AbstractBeanDefinition,BeanDefinition 是配置文件 <bean>
元素标签在容器中的内部表示形式。<bean>
标签拥有配置属性,在 BeanDefinition 中则有一一的对应关系(如class -> beanClass、lazy-init -> lazyInit 等),RootBeanDefinition 是最常用的实现类,GenericBeanDefinition 是自 2.5 版本后新加入的 bean 标签配置属性定义类,是一站式服务类。
在配置文件中可以定义父子 bean,父 bean 用 RootBeanDefinition(没有父 bean 的 bean,也用它表示) 表示,子 bean 用 ChildBeanDefinition 表示,AbstractBeanDefinition 是对两者共同的类信息进行的抽象类。**Spring 将这些解析好的 BeanDefinition 注册到 BeanDefinitionRegistry 中,BeanDefinitionRegistry 就像 Spring 配置信息的内存数据库,主要以 map 形式保存。**他们之间的关系图如下:
所以解析属性会先创建承载属性的实例,即 GenericBeanDefinition 实例,这就是 createBeanDefinition 方法干的事
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();
// parentName 可能为空
bd.setParentName(parentName);
if (className != null) {
//如果 classLoader 不为空,则使用传入的 classLoader 同一虚拟机加载类对象
// 否则只是记录 className
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
2、解析各种属性
创建好 bean 信息的承载实例后,就开始进行 bean 信息的各种属性解析了,先来看 parseBeanDefinitionAttributes 方法,它是对所有元素属性进行解析
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//singleton 属性已弃用,使用 scope 属性来代替
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();
}
// 除设置为 true 外,其他均为 false
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析 autowire 属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析 depends-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 属性的解析。有不熟悉的属性含义的,请参阅官方文档学习。
3、解析子元素 meta
meta 属性的使用如下:
<bean id="testBean" class="com.luo.spring.guides.helloworld.common.TestBean">
<meta key="testStr" value="aaaaa"/>
</bean>
它是一个额外的声明,当需要使用时,可以通过 BeanDefinition 的 getAttribute(key) 方法获取,对 meta 属性的解析代码如下:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
// 获取当前节点的所有子元素
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//提取 meta
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
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);
}
}
}
4、解析子元素 lookup-method
lookup-method 不常使用,但在某些时候会非常有用,通常我们称它为获取器注入。获取器注入是一种特殊的方法注入,它把一个方法声明为返回某种类型的 bean,但实际要返回的 bean 是在配置文件中配置的,此方法可用在设计有些可插拔的功能上,解除程序依赖,我们来看看具体的应用。
1、首先我们创建一个父类
package com.luo.spring.guides.helloworld.lookupmethod;
/**
* @author : archer
* @date : Created in 2022/10/14 11:04
* @description :
*/
public class User {
public void showMe(){
System.out.println("i am user");
}
}
2、创建其子类,并覆盖showMe方法
package com.luo.spring.guides.helloworld.lookupmethod;
/**
* @author : archer
* @date : Created in 2022/10/14 11:05
* @description :
*/
public class Teacher extends User{
@Override
public void showMe() {
System.out.println("i am teacher");
}
}
3、创建调用方法
package com.luo.spring.guides.helloworld.lookupmethod;
/**
* @author : archer
* @date : Created in 2022/10/14 11:07
* @description :
*/
public abstract class GetBeanTest {
public void showMe(){
this.getBean().showMe();
}
public abstract User getBean();
}
4、创建测试方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="getBeanTest" class="com.luo.spring.guides.helloworld.lookupmethod.GetBeanTest">
<lookup-method name="getBean" bean="teacher"/>
</bean>
<bean id="teacher" class="com.luo.spring.guides.helloworld.lookupmethod.Teacher"/>
</beans>
Main.java
package com.luo.spring.guides.helloworld.lookupmethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/10/14 11:10
* @description :
*/
public class Main {
public static void main(String[] args) {
ApplicationContext bf = new ClassPathXmlApplicationContext("test/lookup/lookupTest.xml");
GetBeanTest test = (GetBeanTest) bf.getBean("getBeanTest");
test.showMe();
}
}
输出结果
i am a teacher
当我们业务发生变更,需要修改 showMe 的逻辑时,只需要增加新的逻辑类,如下:
package com.luo.spring.guides.helloworld.lookupmethod;
/**
* @author : archer
* @date : Created in 2022/10/14 11:24
* @description :
*/
public class Student extends User{
@Override
public void showMe() {
System.out.println("i am a 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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="getBeanTest" class="com.luo.spring.guides.helloworld.lookupmethod.GetBeanTest">
<lookup-method name="getBean" bean="student"/>
</bean>
<bean id="student" class="com.luo.spring.guides.helloworld.lookupmethod.Student"/>
</beans>
运行测试类输出结果:
i am a 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 的子元素下,且为 <look-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 override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
5、解析子元素 replaced-method
这个方法主要是对 bean 中 replaced-method 子元素的提取,先来看看这个元素的用法。
方法替换:可以在运行时用新的方法替换现有的方法。它不但可以动态的替换返回实体 bean,还可以动态地更改原有方法的逻辑。
示例如下:
1、在 changeMe 中完成某个业务的逻辑
package com.luo.spring.guides.helloworld.replacedmethod;
/**
* @author : archer
* @date : Created in 2022/10/14 11:50
* @description :
*/
public class TestChangeMethod {
public void changeMe(){
System.out.println("changeMe");
}
}
2、运营一段时间后,需要改变原有的业务逻辑
package com.luo.spring.guides.helloworld.replacedmethod;
import org.springframework.beans.factory.support.MethodReplacer;
import java.lang.reflect.Method;
/**
* @author : archer
* @date : Created in 2022/10/14 11:52
* @description :
*/
public class TestMethodReplacer implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("我替换了原方法");
return null;
}
}
3、使替换后的类生效
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testChangeMethod" class="com.luo.spring.guides.helloworld.replacedmethod.TestChangeMethod">
<replaced-method name="changeMe" replacer="replacer"/>
</bean>
<bean id="replacer" class="com.luo.spring.guides.helloworld.replacedmethod.TestMethodReplacer"/>
</beans>
4、测试
package com.luo.spring.guides.helloworld.replacedmethod;
import com.luo.spring.guides.helloworld.lookupmethod.GetBeanTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/10/14 11:57
* @description :
*/
public class Main {
public static void main(String[] args) {
ApplicationContext bf = new ClassPathXmlApplicationContext("test/replaced/replacedMethodTest.xml");
TestChangeMethod test = (TestChangeMethod) bf.getBean("testChangeMethod");
test.changeMe();
}
}
输出结果:
我替换了原方法
知道了用法后,我们再来看下元素的提取过程:
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 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));
overrides.addOverride(replaceOverride);
}
}
}
无论是 lookup-method 还是 replaced-method 都是构造了一个 MethodOverride,我们后续章节再来介绍它的用法
6、解析子元素 constructor-arg
constructor-arg 是对构造函数的解析,简单用法如图:
解析使用的是 parseConstructorArgElements 函数,具体代码如下:
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析 constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
parseConstructorArgElement 代码如下:
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);
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 valueHolder = new ConstructorArgumentValues.ValueHolder(value);
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 {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
// 没有 index 属性,则忽略 index 属性
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
上述代码逻辑大致如下:
- a、如果配置了 index 属性
- 1、解析 constructor-arg 子元素
- 2、使用 ConstructorArgumentValues.ValueHolder 类型来封装解析出来的元素(type、name 和 index)
- 3、将封装好的 ConstructorArgumentValues.ValueHolder 类型实例,添加到当前 BeanDefinition 的 constructorArgumentValues 的 indexedArgumentValues 属性中
- b、如果未配置 index 属性
- 1、解析 constructor-arg 子元素
- 2、使用 ConstructorArgumentValues.ValueHolder 类型来封装解析出来的元素(type、name)
- 3、将封装好的 ConstructorArgumentValues.ValueHolder 类型实例,添加到当前 BeanDefinition 的 constructorArgumentValues 的 genericArgumentValues 属性中
可以看出无论有没有 index 属性,Spring 的处理过程大致相同,只是存在的位置不同而已。接下来我们来看 parsePropertyValue
@Nullable
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 属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//解析 constructor-arg 上的 value 属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//constructor-arg 上 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);
}
if (hasRefAttribute) {
// ref 属性的处理、使用 RuntimeBeanReference 封装对应的 ref 名称
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
// value 属性的处理、使用 TypedStringValue 封装
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
//解析子元素
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
对于构造函数中又嵌入子元素 map 或 list 等的实现代码在函数 parsePropertySubElement 中,具体如下:
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
//解析 local
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.
//解析 parent
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;
}
//对于 idref 元素的解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//对于 value 子元素的解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//对于 null 子元素的解析
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// 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;
}
//对于 array 子元素的解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//对于 list 子元素的解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//对于 set 子元素的解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//对于 map 子元素的解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//对于 props 子元素的解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
上面实现了所有可支持的子类的分类梳理。
7、解析子元素 property
property 使用方式如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testBean" class="com.luo.spring.guides.helloworld.common.TestBean">
<property name="testStr" value="aaa"/>
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
</bean>
</beans>
具体解析代码如下:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
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;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
可以看到上面是使用 PropertyValue 来进行封装
8、解析子元素 qualifier
对于 qualifier 的使用,我们更多接触的是注解方式,这里我们介绍下 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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testBean" class="com.luo.spring.guides.helloworld.common.TestBean">
<qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="qf"/>
</bean>
</beans>
其解析函数 parseQualifierElements,具体实现代码如下:
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
parseQualifierElement((Element) node, bd);
}
}
}
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
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;
}
}
}
bd.addQualifier(qualifier);
}
finally {
this.parseState.pop();
}
}
可以看到上面是使用 AutowireCandidateQualifier 来进行封装,至此,我们已经完成对 <bean>
标签子元素的全部解析工作。它在 xml 中的所有信息,都能在 GenericBeanDefinition 实例类中找到对应的字段存储。GenericBeanDefinition 是子类实现,大部分通用属性都保存在 AbstractBeanDefinition 中,接下来我们来学习 AbstractBeanDefinition 。
二、AbstractBeanDefinition 属性
我们先来看下 AbstractBeanDefinition 的整体结构
@SuppressWarnings("serial")
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
//省略静态变量以及 final 常量
@Nullable
private volatile Object beanClass;
//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;
//依赖检查,Spring 2.0后已弃用
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
//用来表示一个 bean 的实例化依靠另一个 bean 先实例化,对应 bean 属性 depend-On
@Nullable
private String[] dependsOn;
//autowire-candidate 属性设置为false,这样容器在查找自动装配对象时,
//将不考虑该 bean,即它不会被考虑作为其他 bean 自动装配的候选者,但是该 bean 本身还是可以使用自动装配来注入其他 bean 的
private boolean autowireCandidate = true;
//自动装配时,当出现多个 bean 候选者时,将作为首选者,对应 bean 属性 primary
private boolean primary = false;
//用于记录 Qualifier,对应子元素 qualifier
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
@Nullable
private Supplier<?> instanceSupplier;
//允许访问非公开的过偶早起和方法,程序设置
private boolean nonPublicAccessAllowed = true;
//是否以一种宽松的模式解析构造函数,默认为 true
//若为 false ,则在下列情况下
//interface ITest(){}
//class ITestImpl implements ITest{}
//class Main{
// Main(ITest i){}
// Main(ITestImpl i){}
//}
//程序会抛出异常,因为 Spring 无法确定定位使用哪个构造函数
//程序设置
private boolean lenientConstructorResolution = true;
//对应 bean 属性的 factory-bean
@Nullable
private String factoryBeanName;
//对应 bean 属性的 factory-method
@Nullable
private String factoryMethodName;
//记录构造函数注入属性,对应 bean 属性 constructor-arg
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
//普通属性集合
@Nullable
private MutablePropertyValues propertyValues;
//方法重写的持有者,记录 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;
//定义这个 bean 的应用,
//ROLE_APPLICATION:用户
//ROLE_INFRASTRUCTURE:完全内部使用,与用户无关
//ROLE_SUPPORT 某些复杂配置的一部分
//程序设置
private int role = BeanDefinition.ROLE_APPLICATION;
//bean 的描述信息
@Nullable
private String description;
//这个 bean 定义的资源
@Nullable
private Resource resource;
//省略 set/get 方法
}
三、解析默认标签中的自定义标签
到这里我们已经完成默认标签 <bean>
的解析和提取过程,我们回到解析 bean 的起始函数:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 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);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
分析完 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
这部分代码后,我们来继续往下看
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
从语义上分析它的作用大致是
如果需要的话,就对 beanDefinition 进行装饰
代码如下:
//解析自定义属性,对程序默认的属性是直接略过的
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
//参数 containingBd 是父类 bean,当对嵌套配置进行分析是,需要传递父类 beanDefinition
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;
}
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
//获取自定义标签的命名空间
String namespaceUri = getNamespaceURI(node);
//对非默认标签进行修饰
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;
}
逻辑大致如下:
- 1、首先获取属性或元素命名空间,判断该元素或属性是否适用于自定义标签的解析条件
- 2、如果符合,就找出自定义类型所对应的 NamespaceHandler ,来进行进一步解析
以上我们就完成了对默认标签 <bean>
的解析了,下篇文章我们来学习下它的注册逻辑。