更多文章请观看:www.shangsw.com
一 默认标签的解析
在 1、Spring容器的基本实现 中提到了Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在很大的不同。
默认标签的解析是在parseDefaultElement() 函数中进行的,如下:
/**
* 解析默认的标签元素
* @param ele 元素
* @param delegate BeanDefinitionParserDelegate
*/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {//解析import标签
importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {//解析alias标签
processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {//解析bean标签
processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {//解析beans标签(递归)
doRegisterBeanDefinitions(ele);
}
}
代码很清晰,分别对4中不同标签(import、alias、bean和beans)做了不同的处理。
1.1 bean标签的解析及注册
我们先从bean标签的解析开始,因为bean标签是最复杂也是最重要的。进入函数processBeanDefinition(ele, delegate):
/**
* Bean定义的解析处理
* @param ele bean标签元素
* @param delegate Bean的解析代理
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析Bean标签的元素 Start BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
//解析Bean标签的元素 End
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);//对beanDefinition进行装饰(自定义标签), 如果需要的话
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());//注册Bean实例
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//通知监听器解析及注册完成(Spring没有做任何逻辑, 可以通过程序扩展)
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
大致逻辑如下:
- 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bgHolder,经过这个方法后,bgHolder实例已经包含了配置文件中的各种属性了,如class、name、id、alias之类的属性
- 当返回gbHolder不为空的情况若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析
- 解析完成后,需要对解析后的gbHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法
1.1.1 解析BeanDefinition
首先进入delegate.parseBeanDefinitionElement(ele)方法:
/**
* 解析Bean元素
* @param ele Bean元素
* @return BeanDefinitionHolder
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* 解析Bean元素
* @param ele Bean元素
* @param containingBean Bean容器
* @return BeanDefinitionHolder
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);//解析ID属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//解析name属性
//分隔name属性(以,;分隔) Start
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//分隔name属性(以,;分隔) End
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(AbstractBeanDefinition子类)的实例中 Start
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
//解析其他所有的属性并统一封装到GenericBeanDefinition(AbstractBeanDefinition子类)的实例中 End
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {//beanName不为空
try {
if (containingBean != null) {//如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
} else {
beanName = this.readerContext.generateBeanName(beanDefinition);
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;
}
完成的主要工作如下:
- 提取元素中的id以及name属性
- 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
- 如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName
- 将获取到的信息封装到BeanDefinitionHolder的实例中
我们进一步查看解析其他属性parseBeanDefinitionElement(ele, beanName, containingBean) 方法:
/**
* 解析bean的其他所有属性并封装成GenericBeanDefinition
* @param ele bean标签的元素
* @param beanName beanName
* @param containingBean BeanDefinition
* @return GenericBeanDefinition。当解析错误的时候可能为空
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));//使用ArrayDeque存储beanName的状态
//解析class属性 Start
String className = null;//class属性的值
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
//解析class属性 End
//解析parent属性 Start
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//解析parent属性 End
try {
//用于创建承载属性AbstractBeanDefinition类型的GenericBeanDefinition Start
AbstractBeanDefinition bd = createBeanDefinition(className, parent);//只有parent和className或className对应的对象实例
//用于创建承载属性AbstractBeanDefinition类型的GenericBeanDefinition End
//硬编码解析bean的各种属性 Start
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//硬编码解析bean的各种属性 End
//提取description Start
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//提取description End
parseMetaElements(ele, bd);//解析元数据
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//解析lookup-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//解析replaced-method属性
parseConstructorArgElements(ele, bd);//解析构造函数参数
parsePropertyElements(ele, bd);//解析property子元素
parseQualifierElements(ele, bd);//解析qulifier子元素
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();//弹出beanName的状态
}
return null;
}
1.1.1.1 创建用于属性承载的BeanDefinition
BeanDefinition是一个接口,其实现如下:
BeanDefinition在Spring中有三种实现:RootBeanDefinition、ChildBeanDefinition及GenericBeanDefinition。三种实现都继承了AbstractBeanDefinition,其中BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition和中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。
在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefinition表示,而没有父的就使用RootBeanDefinition表示,AbstractBeanDefinition对两者共同的类信息进行抽象。
Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存库,主要是以Map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。
由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,而代码createBeanDefinition(className, parent) 作用就是实现此功能:
/**
* 创建一个AbstractBeanDefinition, 使用其子类GenericBeanDefinition
* @param className class名字
* @param parentName parent名字
* @return GenericBeanDefinition
* @throws ClassNotFoundException 类不存在异常
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());//创建
}
BeanDefinitionReaderUtils#createBeanDefinition()代码如下:
/**
* 从给定的parentName和className创建一个新的GenericBeanDefinition
* 如果类加载器存在, 则从指定的类加载器创建。否则只是记录beanName
* @param parentName parentName
* @param className className
* @param classLoader 类加载器
* @return GenericBeanDefinition
* @throws ClassNotFoundException 类不存在异常
*/
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) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
} else {
bd.setBeanClassName(className);
}
}
return bd;
}
1.1.1.2 解析各种属性
创建完bean信息的承载实例后,便可以进行beanxinxi的各种属性解析了,进入parseBeanDefinitionAttributes(ele, beanName, containingBean, db)方法:
/**
* 解析Bean的各种属性
* @param ele 给定的bean元素
* @param beanName beanName
* @param containingBean bean容器
* @param bd AbstractBeanDefinition
* @return AbstractBeanDefinition
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//解析scope属性 Start
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {//解析singleton属性, 该属性只是在老版本的1.x中使用, 在后续的版本中已经废弃, 错误日志
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
} else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {//解析scope属性
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
} else if (containingBean != null) {//解析scope属性
//在嵌入BeanDefinition情况下且没有单独指定scope属性则使用父类默认的属性
bd.setScope(containingBean.getScope());
}
//解析scope属性 End
//解析abstract属性 Start
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//解析abstract属性 End
//解析lazy-init属性 Start
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//解析lazy-init属性 End
//解析autowire属性 Start
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//解析autowire属性 End
//解析depends-on属性 Start
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
//解析depends-on属性 End
//解析autowire-candidate属性 Start
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));
}
//解析autowire-candidate属性 End
//解析primary属性 Start
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
//解析primary属性 End
//解析init-method属性 Start
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);
}
//解析init-method属性 End
//解析 destroy-method 属性 Start
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);
}
//解析 destroy-method 属性 End
//解析 factory-method 属性 Start
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
//解析 factory-method 属性 End
//解析 factory-bean 属性 Start
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
//解析 factory-bean 属性 End
return bd;
}
这个方法里面没有业务逻辑,主要是解析bean的属性,然后存放到GenericBeanDefinition中。
1.1.1.3 解析子元素meta
在开始解析元数据之前,meta的属性如下:
<bean id="myTestBean" class="bean.MyTestBean">
<meta key="testKey" value="testValue">
</bean>
这段代码不会体现在MyTestBean的属性中,而是一个额外的声明,当需要使用里面的信息的时候可以通过BeanDefinition的getAttribute(key) 方法获取:
/**
* 解析子元素meta标签
* @param ele Element
* @param attributeAccessor BeanMetadataAttributeAccessor
*/
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)) {//meta标签
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);//key
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);//value
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);//使用key-value构造BeanMetadataAttribute
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);//存储信息
}
}
}
1.1.1.4 解析子元素lookup-method
子元素lookup-method似乎并不是很常用,通常我们称之为获取器注入。获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里面配置的。此方法可用在设计有些可插拔的功能上,我们看一个具体实例:
(1) 首先创建一个父类
public class User {
public void showMe() {
System.out.println("I am a user");
}
}
(2) 创建其子类并覆盖showMe方法
public class Teacher extends User {
public void showMe() {
System.out.println("I am a teacher");
}
}
(3) 创建调用方法
public abstract class GetBeanTest {
public void showMe() {
this.getBean().showMe();
}
public abstract User getBean();
}
(3) 配置文件如下
<bean id="getBeanTest" class="test.GetBeanTest">
<lookup-method name="getBean" bean="teacher"/>
</bean>
<bean id="teacher" class="test.Teacher"/>
(4)测试方法如下
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("lookupTest.xml");
GetBeanTest test = context.getBean(GetBeanTest.class);
test.showMe();
}
}
最后运行的效果如下:
I am a teacher
此时如果我想换一个实现,改变lookup-method的配置即可。
如下,是解析lookup-method的源码:
/**
* 解析lookup-method子元素
* @param beanEle Element
* @param overrides 子元素
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);//方法名
String beanRef = ele.getAttribute(BEAN_ELEMENT);//bean名称
LookupOverride override = new LookupOverride(methodName, beanRef);//构造成LookupOverride
override.setSource(extractSource(ele));
overrides.addOverride(override);//存储
}
}
}
代码与parseMetaElements大同小异,最后通过使用LookupOverride类型的实体来进行数据承载并记录在AbstractBeanDefinition中的methodOverrides属性中。
1.1.1.5 解析子元素replacedmethod
这个方法是针对bean中的replaced-method子元素的提取,功能是:可以在运行时用心的方法替换现有的方法。我们先看一个实例:
(1) 在changeMe中完成某个业务逻辑
public class TestChangeMethod {
public void changeMe() {
System.out.println("changeMe");
}
}
(2) 一段时间后需要改变原有逻辑
public class TestMethodReplacer implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("我替换了原有的方法");
}
}
(3) 配置文件
<bean id="testChangeMethod" class="test.TestChangeMethod">
<replaced-method name="changeMe" replacer="replacer"/>
</bean>
<bean id="replacer" class="test.TestMethodReplacer"/>
(4) 测试如下
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(replaceMethodTest.xml);
TestChangeMethod test = context.getBean(TestChangeMethod.class);
test.changeMe();
}
测试结果如下:
我替换了原有的方法
知道这个标签的用途之后,我们再看其源码:
/**
* 解析replaced-method子元素的标签
* @param beanEle Element
* @param overrides MethodOverrides
*/
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
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);
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);
}
}
}
我们可以看到无论是look-up还是replaced-method都是构造了一个MethodOverride,并最终记录在AbstractBeanDefinition中的methodOverrides属性中。
1.1.1.6 解析子元素constructor-arg
对构造函数的解析是非常常用的,同时也是非常复杂的。首先,我们看一个简单的例子:
<beans>
<bean id="helloBean" class="test.HelloBean">
<constructor-arg index="0">
<value>张三</value>
</constructor-arg>
<constructor-arg index="1">
<value>你好</value>
</constructor-arg>
</bean>
</beans>
上面实现的功能是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。
对于constructor-arg子元素的解析,Spring是通过parseConstructorArgElements 函数来实现的,代码如下:
/**
* 解析constructor-arg子标签
* @param beanEle Element
* @param bd BeanDefinition(GenericBeanDefinition)
*/
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)) {
parseConstructorArgElement((Element) node, bd);//解析constructor-arg
}
}
}
遍历所有子元素,提取constructor-arg,然后进行解析,解析的函数是parseConstructorArgElement中,代码如下:
/**
* 解析constructor-arg元素
* @param ele Element
* @param bd BeanDefinition(GenericBeanDefinition)
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);//index属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);//type属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//name属性
if (StringUtils.hasLength(indexAttr)) {//有index属性
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));//保存状态
Object value = parsePropertyValue(ele, bd, null);//解析ele对应的属性元素
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属性, 自动寻找
try {
this.parseState.push(new ConstructorArgumentEntry());//保存状态
Object value = parsePropertyValue(ele, bd, null);//解析construct-arg的子元素
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();//弹出状态
}
}
}
上面代码很多,但是逻辑并不复杂,首先是提取constructor-arg上必要的属性(index、type、name)。
- 如果配置中指定了index属性,那么操作步骤如下:
- 解析constructor-arg的子元素
- 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
- 将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前beanDefinition的constructorArgumentValues的indexedArgumentValues属性中
- 如果没有指定index属性,那么操作步骤如下:
- 解析constructor-arg的子元素
- 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
- 将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前BeanDefinition的constructorArgumentValues的genericArgumentValues属性中
可以看到,对于是否制定index属性来说,Spring的处理流程是不同的,关键在于属性信息被保存的位置。
整个流程了解后,我们进入解析构造函数配置中子元素的过程,进入parsePropertyValue()函数:
/**
* 解析construct-arg标签的元素。并封装成相关的类
* @param ele construct-arg
* @param bd BeanDefinition(GenericBeanDefinition)
* @param propertyName 属性名(null)
* @return 如果是ref属性则返回RuntimeBeanRefrence; 如果是value属性则返回TypedStringValue; 解析错误的情况下返回null
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
NodeList nl = ele.getChildNodes();//一个属性只能对应一种类型: ref, value, list等
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) {//对应description或者meta不处理
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
} else {
subElement = (Element) node;
}
}
}
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);//解析construct-arg上的ref属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);//解析construct-arg上的value属性
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
/*
* 在construct-arg上不存在以下两种情况
* 1、同时既有ref属性又有value属性
* 2、存在ref属性或value属性且又有子元素
*/
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {//ref属性的处理
String refName = ele.getAttribute(REF_ATTRIBUTE);//获取ref属性
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);//封装到RuntimeBeanRefrence中
ref.setSource(extractSource(ele));
return ref;
} else if (hasValueAttribute) {//value属性的处理
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));//获取value属性并封装到TypedStringValue中
valueHolder.setSource(extractSource(ele));
return valueHolder;
} else if (subElement != null) {//子元素处理
return parsePropertySubElement(subElement, bd);
} else {//错误的形式
//没有ref、value和子元素, Spring记录错误日志
error(elementName + " must specify a ref or value", ele);
return null;
}
}
从代码上看,对构造函数中属性元素的解析,流程如下:
- 略过description或者meta
- 提取constructor-arg上的ref和value属性,以便于根据规则验证正确性,其规则为在constructor-arg上不存在一下情况:
- 同时既有ref属性又有value属性
- 存在ref属性或者value属性且又有子元素
- ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
- value属性的处理。使用TypedStringValue封装
- 子元素处理。如
<constructor-arg>
<map>
<entry key="key" value="value"/>
</map>
</constructor-arg>
而对于子元素的处理,使用函数parsePropertySubElement()处理,如下:
/**
* 解析construct-arg中的value、ref或者子construct-arg属性或者construct-arg元素
* @param ele 子元素
* @param bd BeanDefinition
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
/**
* 解析value、ref或集合子元素或construct-arg元素。bean启动来说只存在construct-arg的子元素
* @param ele 子元素属性
* @param bd 当前bean的定义
* @param defaultValueType 默认的类型
*/
@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)) {//对ref元素的解析
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);//获取ref的bean元素的属性
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//解析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;
} 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子元素解析
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.1.1.7 解析子元素property
parsePropertyElement()函数完成了对property属性的提取,property使用方式如下:
<bean id="test" class="test.TestClass">
<property name="testStr" value="aaa"/>
</bean>
或者
<bean id="a">
<property name="p">
<list>
<value>aa</value>
<value>bb</value>
</list>
</property>
</bean>
具体解析过程如下:
/**
* 解析property子元素
* @param beanEle property子元素
* @param bd BeanDefinition(GenericBeanDefinition)
*/
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);
}
}
}
提取property子元素,然后调用parsePropertyElement() 函数处理:
/**
* 解析property子元素
* 正常解析没有问题后, 将name和value封装成PropertyValue对象, 并存放到BeanDefinition的MutablePropertyValues中
* @param ele Element
* @param bd BeanDefinition
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);//解析name属性
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));//push状态
try {
if (bd.getPropertyValues().contains(propertyName)) {//不允许多次对同一属性配置
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);//解析value
PropertyValue pv = new PropertyValue(propertyName, val);//封装成PropertyValue
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();//弹出状态
}
}
使用PropertyValue封装,记录在BeanDefinition中的propertyValues属性中。
1.1.1.8 解析子元素qualifier
对于qualifier元素的提取,我们接触更多的是注解形式,在使用Spring框架自动注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个,当找不到一个匹配的Bean时,Spring容器将抛出BeanCreationException异常。解析过程与上述大同小异,不在重复叙述。
1.1.2 AbstractBeanDefinition属性
GenericBeanDefinition是AbstractBeanDefinition子类实现,而大部分的通用属性都保存在AbstractBeanDefinition中,如下,是AbstractDefinition的部分属性:
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
/**
* bean的实体对象
*/
@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 3.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 TestImpl implements ITest {};
* class Main {
* Main(ITest test){};
* Main(TestImpl testImpl){};
* }
* 抛出异常, 因为Spring无法准确定位哪个构造函数
* 程序设置
*/
private boolean lenientConstructorResolution = true;
/**
* 对应bean属性的factory-bean, 用法:
* <bean id = "instanceFactoryBean" class="com.shangsw.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;
/**
* 普通属性集合
*/
@Nullable
private MutablePropertyValues propertyValues;
/**
* 方法重写的持有者, 对应lookup-method、replaced-method元素
*/
private MethodOverrides methodOverrides = new MethodOverrides();
/**
* 初始化方法, 对应bean属性的init-method
*/
@Nullable
private String initMethodName;
/**
* 销毁方法, 对应bean属性的destory-method
*/
@Nullable
private String destroyMethodName;
/**
* 是否执行init-method, 程序设置
*/
private boolean enforceInitMethod = true;
/**
* 是否执行destory-method, 程序执行
*/
private boolean enforceDestroyMethod = true;
/**
* 是否是用户定义的而不是应用程序本身定义的, 创建AOP时候为true, 程序设置
*/
private boolean synthetic = false;
/**
* 定义这个bean的应用, APPLICATION: 用户; INFRASTRUCTURE: 完全内部使用, 与用户无关; SUPPORT: 某些复杂配置的一部分
*/
private int role = BeanDefinition.ROLE_APPLICATION;
/**
* bean的描述信息
*/
@Nullable
private String description;
/**
* 这个bean定义的XML资源
*/
@Nullable
private Resource resource;
}
1.1.3 解析默认标签中的自定义标签元素
此时我们已经解析完bean标签了,我们再回到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition方法,下面,我们要进行delegate.decorateBeanDefinitionIfRequired(ele, bdHolder) 代码的分析,
这段代码主要适用于这样的场景,如:
<bean id="test" class="test.MyClass">
<mybean::user username="aaa">
</bean>
当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这段代码就会起作用。而这里的自定义标签解析与上面的自定义标签解析不同,上面针对的是bean的自定义标签解析。
代码如下:
/**
* 装饰指定的BeanDefinition, 如果有需要的话
* @param ele bean标签元素
* @param originalDef 当前的bean definition
* @return BeanDefinitionHolder
*/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
第三个参数是父类的bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition。这里分析的是顶层配置,所以传递null:
/**
* 装饰指定的BeanDefinition, 如果有需要的话
* @param ele bean标签元素
* @param originalDef 当前的bean definition
* @param containingBd 这个参数其实是父类的BeanDefinition, 主要是当子类的bean没有指定scope时使用父类的scope
* @return BeanDefinitionHolder
*/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;//解析过的BeanDefinition
//遍历所有的属性, 看看是否有适用于装饰的属性 Start
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
//遍历所有的属性, 看看是否有适用于装饰的属性 End
//遍历所有的子元素, 看看是否有适用于装饰的子元素 Start
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);
}
}
//遍历所有的子元素, 看看是否有适用于装饰的子元素 End
return finalDefinition;
}
上面的代码,我们看到函数分别对元素的所有属性以及子节点进行了decorateIfRequired()函数的调用,如下:
/**
* 如果有装饰的属性, 则处理装饰的属性
* @param node 属性节点
* @param originalDef 解析过的BeanDefinition
* @param containingBd BeanDefinition
* @return BeanDefinitionHolder
*/
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 {
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析。
1.1.4 注册解析的BeanDefinition
对于配置文件,解析完了,装饰也完成了,对于得到的beanDefinition已经可以满足后续的使用要求了,唯一剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 代码的解析了。
/**
* 通过给定的BeanDefinitionHolder和BeanFactory注册Bean的实例
* @param definitionHolder bean定义, 包含beanName及其别名
* @param registry 要注册的beanFactory
* @throws BeanDefinitionStoreException Bean定义存储异常
*/
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
//通过beanName注册BeanDefinition Start
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//通过beanName注册BeanDefinition End
//注册所有的别名 Start
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
//注册所有的别名 End
}
解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而对于beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。
1.1.4.1 通过beanName注册BeanDefinition
registerBeanDefinition(beanName, beanDefinition) 函数代码如下:
/**
* 注册beanDefinition
* @param beanName bean的名称
* @param beanDefinition BeanDefinition
* @throws BeanDefinitionStoreException BeanDefinition存储异常
*/
@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");
//注册前的最后一次校验 Start
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/*
* 主要是对于AbstractBeanDefinition属性中的methodOverrides校验,
* 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex);
}
}
//注册前的最后一次校验 End
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {//已经在beanDefinitionMap中存在
if (!isAllowBeanDefinitionOverriding()) {//不允许覆盖, 直接抛出异常
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
} else if (existingDefinition.getRole() < beanDefinition.getRole()) {
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 + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);//允许覆盖
} else {//在beanDefinitionMap中不存在
if (hasBeanCreationStarted()) {//如果这个beanDefinition已经存在了
//由于beanDefinitionMap是全局变量, 这里会有并发访问的情况, 所以这里加锁
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);//将bean实例存放到beanDefinitionMap中
//更新存放beanName的集合 Start
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//更新存放beanName的集合 End
removeManualSingletonName(beanName);//删除单例
}
} else {//这个bean没有创建
this.beanDefinitionMap.put(beanName, beanDefinition);//存放到缓存中
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);//重置所有beanName对应的缓存
} else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
对bean的注册处理,主要有几个步骤:
- 对AbstractBeanDefinition的校验。在解析XML文件时是对XML格式的校验,而此时的校验是针对AbstractBeanDefinition的methodOverrides属性的
- 对beanName已经注册的情况处理,如果设置了不允许bean的覆盖,则抛出异常,否则直接覆盖
- 加入map缓存
- 清除解析之前留下的对应beanName的缓存
1.1.4.2 通过别名注册BeanDefinition
registerAlias(name, alias) 是别名的注册:
/**
* 注册beanName和alias的对应关系
* @param name beanName
* @param alias 别名
*/
@Override
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) {
if (alias.equals(name)) {//如果beanName和alias是一样的
this.aliasMap.remove(alias);//删除alias
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
} else {//beanName和alias是不一样
String registeredName = this.aliasMap.get(alias);//获取是否存在这个alias
if (registeredName != null) {//存在alias
if (registeredName.equals(name)) {//如果两者相同则不作处理
return;
}
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 + "'");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);//存放alias和beanName的对应关系
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
注册alias的步骤如下:
- alias与beanName相同情况处理,若alias与beanName名称相同则不需要处理并删除掉原有alias
- alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要用户设置进行处理
- alias循环检查,当A->B存在是,若再次出现A->C->B时候则会抛出异常
- 注册alias
1.1.5 通知监听器解析及注册完成
通过你代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)) 完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在Spring中并没有对此事件做任何逻辑处理。
1.2 alias标签的解析
在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用alias标签来指定。而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件都更容器对公共组件进行引用。
alias标签的结果如下:
/**
* 解析并注册alias标签
* @param ele alias标签
*/
protected void processAliasRegistration(Element ele) {
String name = ele.getAttribute(NAME_ATTRIBUTE);//获取beanName
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);//获取alias
//数据为空校验 Start
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
//数据为空校验 End
if (valid) {//校验没有问题
try {
getReaderContext().getRegistry().registerAlias(name, alias);//注册alias
} catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
跟之前bean中的alias解析大同小异,都是将别名与beanName组成一对注册至registry中。这里不仔赘述。
1.3 import标签的解析
对于Spring配置文件的编写,有时候有很多配置文件,而最后可以使用import进行引入,这样可以对配置文件进行模块化,易于管理。
如下,是Spring解析import配置文件:
/**
* import标签解析
* @param ele import标签
*/
protected void importBeanDefinitionResource(Element ele) {
//获取resource属性 Start
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);//获取resource属性
if (!StringUtils.hasText(location)) {//不存在则不做处理
getReaderContext().error("Resource location must not be empty", ele);
return;
}
//获取resource属性 End
//解析系统属性, 格式如: ${user.dir}
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
//判断location是绝对URI还是相对URI Start
boolean absoluteLocation = false;//判断localtion是绝对URI还是相对URI
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();//判断
} catch (URISyntaxException ex) {
}
//判断location是绝对URI还是相对URI End
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 {//相对URI, 根据相对URI计算出绝对的URI
try {
int importCount;
/*
* 根据相对UIR创建资源, Resource存在多个子类, 如VfsResource、FileSystemResource等,
* 而每个子类的createRelative方式实现都不一样, 所以这里先使用子类的方法尝试解析
*/
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {//解析成功
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);//加载beanDefinitions
actualResources.add(relativeResource);
} else {//解析不成功
//使用默认解析器ResourcePatternResolve进行解析
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));
}
代码流程也很清晰,Spring进行解析的步骤大致如下:
- 获取resource属性所表示的路径
- 解析路径中的系统属性,格式如“${user.dir}”
- 判定location是绝对路径还是相对路径
- 如果是绝对路径则递归调用bean的解析过程,进行另一次的解析
- 如果是相对路径则计算出绝对路径并进行解析
- 通知监听器,解析完成
1.4 嵌入式beans标签的解析
对于嵌入式的beans标签,其实没有太多可讲的,与单独的配置文件并没有太大差别,无非就是递归调用beans的解析过程。可自行研究。
而对于自定义标签的解析,我这里就不做过多的介绍了,有兴趣的可以自行阅读