likes
comments
collection
share

Spring 源码学习笔记(二)之Bean标签默认属性的解析

作者站长头像
站长
· 阅读数 5

分析调用栈

Spring 通过读取 xml 配置文件注册 bean ,通过工厂可以获取注册的 bean,示例代码:

XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
Object main = beanFactory.getBean("main");
System.out.println(main);

配置文件 applicationContext.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="main" class="com.pine.Main" />
</beans>

在上述 XmlBeanFactory 中有一个解析器,通过该解析器解析 xml 配置文件: Spring 源码学习笔记(二)之Bean标签默认属性的解析

BeanDefinition 类型是 bean 标签在 JVM 中的存在形式

进入loadBeanDefinitions方法中,重点是331行开始的这一段:

try {
    // 获取配置文件的输入流
    InputStream inputStream = encodedResource.getResource().getInputStream();
    try {
        // InputSource 用于 xml 解析,类似工具类
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        // 执行解析逻辑
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
    finally {
        inputStream.close();
    }
}

在 Spring 源码中有一个惯例,就是 do... 方法一般是真正执行具体逻辑的方法

继续进入 doLoadBeanDefinitions 方法,可以看到是一个 try 后面很多 catch ,只需要关注 try 里的内容就可以了,catch里无非是一些异常处理。这个方法中需要关注的是 registerBeanDefinitions 方法,继续进入它的重载方法,再来到 doRegisterBeanDefinitions 方法。 这个方法中主要有两个部分,上面的 if 块和下面的 parseBeanDefinitions 方法。 if 块的作用是处理不同的 profile (多环境),parseBeanDefinitions 才是解析标签。 这个方法的注释及方法体如下:

/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    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)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

bean 标签是 Spring 中默认自带的元素,所以先看 parseDefaultElement 方法,再进入 processBeanDefinition 方法,继续来到 parseBeanDefinitionElement 方法。

分析解析方法

至此,终于来到了最终执行解析逻辑的方法! 1)首先是前两行:

// 解析 id 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析 name 属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

很浅显易懂,就是解析 id 和 name 这两个属性。

2)然后是处理别名:

// 处理别名
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
    String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    aliases.addAll(Arrays.asList(nameArr));
}

3)接着兼容了 bean 标签中没有 id 的情况(这里也说明了为什么 bean 可以不指定 id):

String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    beanName = aliases.remove(0);
    ……
}

4)然后是当前方法的重载方法 parseBeanDefinitionElement, 4.1)这个方法中首先解析了 class 和 parent 属性:

String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    parent = ele.getAttribute(PARENT_ATTRIBUTE);
}

4.2)然后是 lookup-method、replaced-method 等子标签:

// meta
parseMetaElements(ele, bd);
// lookup-method
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// replaced-method
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

// constructor-arg
parseConstructorArgElements(ele, bd);
// property
parsePropertyElements(ele, bd);
// qualifier
parseQualifierElements(ele, bd);

5)最后是一个 if 块,这个 if 块主要是处理了 id , name 都为空的情况,会根据算法为当前 bean 生成一个名字,这里不细究。

这样,就搞清楚了 xml 配置文件中 bean 标签是如何解析的。

欢迎关注微信公众号co松柏一起学习交流!

转载自:https://juejin.cn/post/7355307547480735785
评论
请登录