前言
在上一篇中,最后讲到了parseDefaultElement方法和parseCustomElement方法,对标签进行解析。先讲一下对bean标签的解析。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
- 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,此时,bdHolder就已经包含配置文件中的配置的各种属性了,例如:class、name、id
- 当返回的bdHolder不为空时若存在默认标签的子节点下再有自定义属性,还需要对自定义标签进行解析。
- 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerDefinition方法。
- 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。
下面依次解释每个步骤的含义。
解析BeanDefinition
先解释一个类BeanDefinitionHolder,
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition;
private final String beanName;
@Nullable
private final String[] aliases;
public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
Assert.notNull(beanName, "Bean name must not be null");
this.beanDefinition = beanDefinition;
this.beanName = beanName;
this.aliases = aliases;
}
从上面可以看出,BeanDefinition是BeanDefinition的一个持有者,并存储bean的姓名和别名。
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//分割name属性
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
主要步骤:
- 提取元素中的id以及name属性
- 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型中的实例中。
- 如果监测到bean没有指定的beanName,那儿使用默认规则为此Bean生成beanName
- 将获取到的信息封装到BeanDefinitionHolder的实例中。
先简单介绍BeanDefinition这个接口。
/**
* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
*
上面是源码文档解释,从上可以看出,它描述了一个bean实例,有属性值和构造函数参数值,具体的信息由具体的子类来实现。
在配置文件中<bean>元素拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、Scope、lazyInit属性,BeanDefinition和<bean>中的元素一一对应。
具体的类RootBeanDefinition、GenericBeanDefinition和ChildBeanDefinition。
其中RootBeanDefinition是最常用的类,在配置文件中可以配置父<bean>和子<bean>,子<bean>用childBeanDefinition表示,没有子类时,直接用RootBeanDefinition表示。
Spring通过BeanDefinition将配置文件中的<bean>配置信息转化为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器中的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。
BeanDefinitionParserDelegate
* Stateful delegate class used to parse XML bean definitions.
* Intended for use by both the main parser and any extension
从上面的定义中可以看出它是代表类,用于处理XML Bean定义的类,干的都是脏活累活。
import/alias/bean等element以及element的子节点以及属性都是它解析并且填充到BeanDefinition中然后使用ReaderContext中的Registry(实际就是DefaultListableBeanFactory)来将该BeanDefinition注册。
Bean的注册
Spring提供了BeanFactory对Bean进行获取,但Bean的注册和管理并不是在BeanFactory中进行,而是在BeanDefinitionRegistry中进行,这里BeanFactory只提供了查阅的功能。
Spring的Bean信息注册保存在一个个BeanDefinition中的。
我们以ClassPathXmlApplicationContext为例
- 在它的构造函数中主要的逻辑方法有两个。
首先调用setConfigLocations()来设置配置文件路径并可以修改配置文件中的属性值。
然后调用refresh()
方法。它是代码的核心,用来对Bean进行注册和初始化。 - 在refresh()方法中,主要有下面几个步骤
- BeanFactory的初始化,并且加载配置文件中相关的bean信息。
- 调用postProcessBeanFactory(beanFactory)抽象方法,用于供给子类对已经生成的BeanFactory的一些信息进行定制,registerBeanPostProcessors对BeanPostProcessor进行注册。
BeanPostProcessor是一个扩展点,有两个方法,分别对应IOC容器对对象初始化前的操作和初始化后的操作。 - 初始化国际化信息
- 注册和调用相关的监听器
- 实例化注册的bean信息
- 对于bean的注册,我们需要关注refreshBeanFactory()方法,
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { // 如果BeanFactory已经创建则对其进行销毁
destroyBeans();
closeBeanFactory();
}
try {
// 创建BeanFactory实例
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId()); // 为当前BeanFactory设置一个标识id
customizeBeanFactory(beanFactory); // 设置BeanFacotry的定制化属性信息
loadBeanDefinitions(beanFactory); // 加载xml文件信息
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
从中可以看出中间最重要的方法是loadBeanDefinitions
将加载XML文件中的bean信息交给XmlBeanDefinitionReader来处理。
它会依次读取每个xml配置文件中的bean信息,
将xml文件转化为一个InputStream,再转化为InputSource,进而转化为一个Document对象,该对象保存着各个XML文件中各个节点和子节点的相关信息,然后获取到Document的根节点信息,调用BeanDefinitionDocumentReader.registerBeanDefinitions(root)
进行注册。
然后开始解析xml文件,封装成BeanDefinition并完成注册,该点在文章开头已经讲解。
网友评论