本篇文章介绍默认标签的解析过程。
解析默认标签在DefaultBeanDefinitionDocumentReader的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)) {
doRegisterBeanDefinitions(ele);
}
}
方法的逻辑很清晰,按默认标签是import、alias、bean、beans四类情况分别处理。其中,bean标签的解析最为复杂,也最为重要。本文仅分析bean标签的解析。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
解析bean的逻辑很清晰的分为以下几步:
1)将bean的各种属性解析到BeanDefinitionHolder中;
2)如果默认标签的子节点下有自定义属性,需要对自定义标签进行解析;
3)对解析后的BeanDefinitionHolder进行注册;
4)发出响应事件,通知相关监听器这个bean已经加载完毕。
接下来要做的,就是对这四个步骤逐一细化。
实际承载解析后的属性的类是BeanDefinition,它是一个接口,在Spring中存在三种实现类:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition,这三个类均继承自AbstractBeanDefinition。在配置文件中可以定义父Bean和子Bean,父Bean用RootBeanDefinition表示,子Bean用ChildBeanDefinition表示。Spring通过BeanDefinition将配置文件中的bean配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。spring容器的BeanDefinitionRegistry就像Spring配置信息的内存数据库,主要以map形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。解析属性首先要创建用于承载属性的实例,就是创建GenericBeanDefinition类型的实例。
public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, 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;
}
spring需要解析N多属性,这里以常见的constructor-arg为例。比如:
<bean id="testCat" class="Cat">
<constructor-arg index="0">
<value>1value>
constructor-arg>
<constructor-arg index="1">
<value>tonyvalue>
constructor-arg>
bean>
BeanDefinitionParserDelegate的parseConstructorArgElements方法用来解析bean的constructor-arg属性。
解析过程:
1)获取bean对应element下所有的childNode,逐一遍历,寻找nodeName是constructor-arg的childNode进行处理;
2)如果constructor-arg元素存在index属性,作为constructor的子元素,value标签的值存储在TypedStringValue实例中,使用该实例构造ConstructorArgumentValues.ValueHolder实例,将该ValueHolder添加到:
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
3)如果constructor-arg元素不存在index属性,和步骤2)唯一不同的,是最终将ValueHolder添加到:
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
解析属性后,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的配置。GenericBeanDefinition是子类实现,大部分通用属性都保存在AbstractBeanDefinition中。
xml解析完毕后,就可以注册了:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,对BeanDefinition的注册分为两部分:通过beanName注册和通过别名注册。
1. 通过beanName注册
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
BeanDefinition oldBeanDefinition;
synchronized (this.beanDefinitionMap) {
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(...);
}
}
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
1)如果beanName已经注册,那就看看是否允许bean的覆盖,如果不允许,抛异常;否则直接覆盖;
2)以beanName做为key,beanDefinition作为value,放置到beanDefinitionMap中。因为beanDefinitionMap是全局变量,所以用synchronized来避免并发访问的问题;
3)清除解析之前留下的对应beanName的缓存;
2.通过别名注册BeanDefinition
1)如果alias与beanName相同,不需要处理,并从aliasMap中移除掉原有alias;
2)在alias不允许被覆盖的情况下,如果aliasName已经使用并且已经指向了另一beanName,则抛出异常;
3)当A->B存在时,若再次出现A->C->B时,则会抛出异常。
4)注册alias。