阅读 157

Spring源码分析(二)xml 配置全链路分析

在上一篇《Spring 源码分析(一)了解Spring容器的刷新逻辑》我们了解了 Spring 容器的刷新逻辑,这一篇我们就里面的 BeanFactory 的创建与xml 配置的解析好好分析一下。

简单实例

我们使用最简单的实例来探寻源码切入的入口。 ​

首先我们在 xml 配置文件中配置 bean 参数:

<bean id="user" class="com.mrlsm.spring.demo.entity.User" scope="singleton" lazy-init="true" primary="true"/>
复制代码

对应的实体类User

public class User {}
复制代码

测试main方法:

ApplicationContext context2 = new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context2.getBean("user");
System.out.println(user);
复制代码

输出结果: image.png 好,实例运行成功,我们开始分析:

源码分析

一、refresh

ClassPathXmlApplicationContext 创建 contxt 对象时,我们发现了会进入 AbstractApplicationContext#refresh() 方法中。

refresh 方法中处理很多,本章节只分析 BeanFactory 的创建。

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 准备此上下文來进行刷新。状态值的更新
		prepareRefresh();
		// 告诉子类刷新内部 bean 工厂。本章节只分析 beanFactory 的创建
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// 注册一些容器中需要的系统 bean 例如:classLoader 等
		prepareBeanFactory(beanFactory);
        
		// ··· 对 beanFactory 的特殊处理
	}
}
复制代码

debug 调试中发现

image.png

在执行完 AbstractApplicationContext**#**obtainFreshBeanFactory() 方法后 容器中已经含有 user 的 BeanDefinition。 所以 我们主要查看 obtainFreshBeanFactory 方法产生的作用是什么。

二、refreshBeanFactory

此时定位到:AbstractRefreshableApplicationContext**#**refreshBeanFactory()

@Override
protected final void refreshBeanFactory() throws BeansException {
    // 是否已经持有 beanFactory 、刷新状态的判断
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
        // 创建一个新的 beanFactory
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
        // 自定义此上下文使用的内部 bean 工厂。
		customizeBeanFactory(beanFactory);
        // 加载所有的 BeanDefinitions,实际解析xml的位置
		loadBeanDefinitions(beanFactory);
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}
复制代码

三、创建DefaultListableBeanFactory

创建一个 DefaultListableBeanFactory (spring的发动机,以list集合的方式操作bean)

类图如下:spring 中很关键的一个类,详情一一分析他的成员方法,在这就不一一阐述了

image.png

四、loadBeanDefinitions

AbstractRefreshableApplicationContext#loadBeanDefinitions()

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// 为给定的 BeanFactory 创建一个新的 XmlBeanDefinitionReader。
	// 这里使用了委托模式,把BeanDefinition的解析委托给了 BeanDefinitionReader
	// 由于我们当前是解析xml,所以是委托给XmlBeanDefinitionReader。
	// 使用注解方式将会委托给AnnotatedBeanDefinitionReader
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	
        // 给BeanDefinitionReader设置了一些的属性
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	
        // 允许子类提供读取器的自定义初始化,然后继续实际加载 bean 定义。
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}
复制代码

那我们继续看:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		reader.loadBeanDefinitions(configResources);
	}
	// 配置信息位置: new ClassPathXmlApplicationContext("xxx") 时传入的
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		// 委托给 Reader 来加载 BeanDefinition
		reader.loadBeanDefinitions(configLocations);
	}
}
复制代码

从指定的 XML 文件加载 bean 定义。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	// ··· 前置判断
	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
		InputSource inputSource = new InputSource(inputStream);
		if (encodedResource.getEncoding() != null) {
			inputSource.setEncoding(encodedResource.getEncoding());
		}
		return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
	// ··· 异常处理
}
复制代码

五、doLoadBeanDefinitions

实际上从指定的 XML 文件加载 bean 定义。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
        // 配置文件的输入流信息加载为 Document 对象 
		Document doc = doLoadDocument(inputSource, resource);
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
	// ··· 异常处理
}
复制代码

六、registerBeanDefinitions

解析并注册 BeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // Document 的解析被委托给了 BeanDefinitionDocumentReader (用于从 XML 文档中读取 bean 定义)。
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 委托 documentReader 解析注册 BeanDefinition,注意这里传入了一个 XmlReaderContext
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // 返回找到的 bean 定义数量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

// 可以看到 XmlReaderContext 的构造器传入了当前类 XmlBeanDefinitionReader
// 而当前类持有 BeanDefinitionRegistry,所以 XmlReaderContext 中持有了一个 BeanDefinitionRegistry
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                                this.sourceExtractor, this, getNamespaceHandlerResolver());
}
复制代码

七、doRegisterBeanDefinitions

继续跟进,在委托 documentReader 解析注册 BeanDefinition 中

protected void doRegisterBeanDefinitions(Element root) {
	// 新建一个关联代理
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);
	···
    // 空实现 允许 xml 可扩展    
	preProcessXml(root);
    // 解析文档中根级别的元素:“import”、“alias”、“bean”。
	parseBeanDefinitions(root, this.delegate);
    // 空实现 允许 xml 可扩展 
	postProcessXml(root);
	this.delegate = parent;
}

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);
	}
}
复制代码

八、parseDefaultElement

在这里我们就只看默认标签的解析了

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // 解析 import 标签,递归解析 import 导入的 xml 的过程,
    // 看看 import 标签的作用 引入其他配置文件
    // 不难发现它是为了将 bean 定义从给定资源加载到 bean 工厂中。
    // 其中的主要方法为 loadBeanDefinitions 
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    // 解析 alias 标签
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    // 解析 bean 标签,主要看这个 bean 的解析
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    // 解析 beans 标签
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // 递归再次进行解析 BeanDefinitions
        doRegisterBeanDefinitions(ele);
    }
}
复制代码

九、processBeanDefinition

定位到 bean 标签的解析

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 具体的解析过程,将会把 bean 标签解析并封装到 BeanDefinition 中
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 对bean标签解析出来的 BeanDefinition 进行装饰
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 注册 BeanDefinition
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        } 
        catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
		}
		// 发送注册事件
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}
复制代码

十、parseBeanDefinitionElement

继续跟进 parseBeanDefinitionElement 方法

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
	String id = ele.getAttribute(ID_ATTRIBUTE);
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	List<String> aliases = new ArrayList<>();
	if (StringUtils.hasLength(nameAttr)) {
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}
	// beanName 默认为 id, 如果不配置id,将会取第一个 name 当做 beanName
	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) {
		// 校验beanName、alias是否重复
		checkNameUniqueness(beanName, aliases, ele);
	}
	// 解析 xml 获取一个 beanDefinition
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {
			// ··· beanName 的生成
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		// 将 beanDefinition 封装成 holder 返回
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}
	return null;
}
复制代码

然后我们跟进到解析 xml 标签属性中

public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, @Nullable BeanDefinition containingBean) {
	this.parseState.push(new BeanEntry(beanName));
	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);
	}

	try {
		// 创建了一个 GenericBeanDefinition
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// 解析bean标签上的属性 scope、abstract、lazy-init 等并将值设置到 BeanDefinition 上
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		// 设置description 属性
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        
		// 解析 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);

		bd.setResource(this.readerContext.getResource());
		bd.setSource(extractSource(ele));
		return bd;
	}
	// ··· catch
	return null;
}
复制代码

这些解析方法进去就很容易看出:

方法实现的就是循环查找标签并设置到 AbstractBeanDefinition 中。

至此,一个默认的 bean 标签就解析完成,并把它含有的所有信息都封装到了一个 BeanDefinition 实例中,然后这个 BeanDefinition 将会注册到我们的 IOC 容器中去,为下一步生成实例做准备。

十一、BeanDefinitionReaderUtils#registerBeanDefinition()

返回至 第九点 processBeanDefinition 中我们发现最后会将上一步生成的实例放到 beanDefinitionMap、beanDefinitionNames 两个容器中。

而 BeanDefinitionReaderUtils#registerBeanDefinition() 方法就是向 Spring IOC 容器注册解析得到的 BeanDefinition,这个是 BeanDefinition 向 Spring IOC 容器注册的入口。

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 alias : aliases) {
            // 注册别名
            registry.registerAlias(beanName, alias);
        }
    }
}

// DefaultListableBeanFactory#registerBeanDefinition()
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {
	// ... 前置大量分支判断和异常处理
    
    // 将当前 beanDefinition 放入下面这两个容器
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName); 
    // 更新 manualSingletonNames 容器
    removeManualSingletonName(beanName);
}
复制代码

小结

从上我们知道了 spring 默认标签 bean 的解析与处理,就是获取到 xml 中 bean 标签里配置的各种属性,封装成一个 BeanDefinition 对象,然后把这个对象存到我们 IOC 容器的 beanDefinitionMap、beanDefinitionNames 中,为之后在 IOC 容器中使用与获取创造条件,即完成 ConfigurableListableBeanFactory 的创建。

本文基于 spring-framework 5.2.10.RELEASE 版本进行分析

文章分类
后端
文章标签