Spring源码学习---Bean的解析和注册

标签分类

bean标签可以分为两种,一种是spring自带的默认标签,另一种就是用户自定义的标签.所以spring针对这两种情况,提供了不同的解析方式.Spring的bean解析过程是在DefaultBeanDefinitionDocumentReader类中.所以首先对Document中的每个Element进行判断属于那种类型,并根据具体的类型进行解析.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//对beans的处理 
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//对bean的处理(默认的命名空间解析)
parseDefaultElement(ele, delegate);
}
else {
//对bean的处理 (用户自定义的命名空间进行解析)
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

默认标签的解析

默认标签时在parseDefaultElement函数中进行.该函数分别对4种不同的标签(import,alias,bean,beans)做不同的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//对import标签的处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//对alias标签的处理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//对bean标签的处理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//对beans 的处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

bean的解析和注册

现在我们进入processBeanDefinition方法了解bean解析和注册流程,流程可以总结如下:

  1. 解析BeanDefinition.首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析.返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含了我们配置文件的各种属性,如class,name,id,alias之类的属性.
  2. 当返回bdHolder不为空的情况下若存在默认标签的子节点下有自定义属性还需要再次定对自定义标签进行解析.
  3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法.
  4. 最后发出效应事件,通知相关的监听器,这个bean已经加载完成了.
    整个流程代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//bean标签的解析过程
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//1.委托BeanDefinitionDelegate类的 decorateBeanDefinitionIfRequired进行元素解析.
//经过该方法后bdHolder实例包含我们配置文件中的各种属性,如class,id等
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//2.如果默认标签的子节点下有自定义属性,需要再次对自定义标签进行解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//3.对解析后的bdHolder进行注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//4.发出响应事件,通知相关的监听器,这个bean已经加载完成 XmlReaderContext
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

解析BeanDefinition

这部分是解析bean标签的主体部分,主要是内容:

  1. 提取元素中的id和name属性. 在配置文件中name属性一般采用”,; “来分隔别名,如果name不为空的话按”,; “切分得到所有的别名.如果id存在的情况直接使用id值作为bean的id.如果从配置文件解析得到的id不存在且name不为空,在从别名在中去一个当做bean的id.
  2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中.该部分主要是解析bean中的所有属性,如scope,factory-method,meta,property等等.并将这些内容封装成GenericBeanDefinition.
  3. 如果检测的bean没有指定beanName,那么用默认规则为此Bean生成beanName.
    如果className(对应的类名)存在的话那么使用className作为id.否在看是否存在父类bean,采用父类bean名字+”$child”作为id;否则看是否存在工厂,如果存在则使用工厂类名+”$created”作为bean的id.否则就抛出BeanDefinitionStoreException异常,即无法生成bean name的异常.如果这三个有一个存在,且已经被注册了,那么spring采用的策略就是在名字后面加”#”以及从0开始的编号,且该编号在这个bean name中没有被使用过.
  4. 将获取到的信息封装到BeanDefinitionHolder的实例中.
    下面将一部分细节代码省略,留下了整体流程代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//1.解析id 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析名字属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//分隔name属性
List<String> aliases = new ArrayList<String>();
//字符串不为空并且长度不为0
if (StringUtils.hasLength(nameAttr)) {
//将name进行按制定格式切分成array
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
....

//2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
//2.如果不存在beanName 那么根据spring中提供的命名规则为当前bean生成对应的beanName
.....
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//4.封装
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

return null;
}

这里笔者将重点讲解解析过程,对于其他部分通过以上的讲解应该都能理解.下面就针对上面的第二步进行细化,首先我们进入parseBeanDefinitionElement方法,看看里面是怎么实现的,代码如下(同样笔者省略了对整体理解无影响的代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
...

try {
String parent = null;
//获取parent的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition. 主要是用来保存从xml解析后的bean,
//作为容器的内部表示.
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//硬编码解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

//解析元数据 meta数据  AbstractBeanDefinition是BeanMetadataAttributeAccessor的子类
parseMetaElements(ele, bd);
//解析lookup-method  一般称为获取器注入 它把一个bean声明为返回某种类型的bean,
//但实际要返回的bean是在配置文件里面配置的,
//此方法可以用在的功能上,解除程序依赖
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;
}
....

return null;
}

这部分解析比较简单,大部分思想就是从Element中获取相关属性,然后保存到GenericBeanDefinition对象中.因此这部分对个别属性的解析进行了介绍,比较简单的读者可以直接阅读代码就可以了.以上代码可以总结如下:
1.创建用于属性承载的BeanDefinition,这里时创建了GenericBeanDefinition来保存been的各种属性.
2.解析各种属性,解析的属性包含这几个:scope, abstract, LAZY_INIT, AUTOWIRE, DEPENDENCY_CHECK, DEPENDS_ON, AUTOWIRE_CANDIDATE, PRIMARY, INIT_METHOD, DESTROY_METHOD, FACTORY_METHOD, FACTORY_BEAN.这里面的实现比较简单,一般就是从配置元素中获取该属性值不为空则设置.如factory-method的获取:

1
2
3
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}

3.解析子元素meta:
4.解析子元素lookup-method :lookup-method是一种特殊的方法注入,它是在配置文件中指定某个方法的返回的bean类型.此方法可用在设计有些可插拔的功能上,接触程序的依赖.
5.解析子元素replaced-method
6.解析子元素constructor-arg
正式解析构造参数:
如果配置中指定了index属性,那么:
1) 解析constructor-arg的子元素
2) 使用constructorArgumentValues.ValueHolder类型来封装解析出来的元素
3) 将type,name和index属性一并封装在ConstructorValues.ValueHolder类型中,并添加至当前BeanDefinition的constructorArgumentValues indexedArgumentValues属性中.
如果没有指定index属性
1) 解析constructor-arg的子元素
2) 使用constructorArgumentValues.ValueHolder类型来封装解析出来的元素
3) 将type,name和index属性一并封装在ConstructorValues.ValueHolder类型中,
并添加至当前BeanDefinition的constructorArgumentValues的genericArgumentValues属性中

  1. 解析子元素property
  2. 解析子元素qualifier

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

第二步就是对默认标签的解析,这里所指的标签是bean中的子元素或属性.该部分只对自定义标签或bean的自定义属性感兴趣,在方法中实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进一步解析.自定义标签的解析留在后面讲解.

注册解析的BeanDefinition

接下来即使对解析的BeanDefinition进行注册,注册过程包括两部分:

  1. BeanDefinition注册
  2. 注册别名alias

通过beanName注册BeanDefinition

  1. 首先对AbstractBeanDefinition进行校验.此时校验主要时针对AbstractBeanDefinition的methodOverrides属性.
  2. 对beanName已经注册的情况的处理.如果设置了不允许覆盖,则需要抛出异常.
  3. 将beanName和BeanDefinition加入map缓冲中.
  4. 重置所有beanName的缓冲.

通过别名注册BeanDefinition

  1. alias与beanName相同时,不处理且删除原有的别名
  2. alias覆盖处理.若aliasName已经被使用了,并且指向的beanName与当前的beanName不同则抛出异常.
  3. alias循环检查.
  4. 注册

参考资料

《spring源码深度解析》

0%