在上一篇《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);
输出结果:
好,实例运行成功,我们开始分析:
源码分析
一、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 调试中发现
在执行完 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 中很关键的一个类,详情一一分析他的成员方法,在这就不一一阐述了
四、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 版本进行分析