本文较硬,Spring源码层级关系较多,以致于我本人都需要时不时地回顾,还希望读者能够耐心看完,相信一定能带来收获~
BeanDefinitionReader学习过程中的关键词:
- location
- BeanDefinitionReader
- Resource
- BeanDefinitionRegistry
- Resourceloader
- DefaultListableBeanFactory
在 《SpringIOC源码解析(3)—— Resource、ResourceLoader、容器之间的微妙关系》文末,将xml文件解析成了Document对象,接下来就该调用registerBeanDefinitions来解析Document对象。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader,这个是实际从XML的DOM树中读取BeanDefiniton
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获取注册表beanDefinitionMap的在本次加载前的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 加载并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 本次加载注册后容器里BeanDefinition的数量减去先前的,即本次加载的BeanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
先看一下createBeanDefinitionDocumentReader方法:
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
该方法会传入一个documentReaderClass实例对象,传入的的class如下:
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的默认实现
registerBeanDefinitions的倒数第三行和倒数第一行代码就揭示了之前说的loadBeanDefinitions如何获取到本次加载的BeanDefinition的数量:
- 加载前先统计下注册表中BeanDefinition的数量
- 加载后再去统计下注册表中BeanDefinition的数量,两者计算差值
统计BeanDefinition的数量的方法:
@Override
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
this就是上面getRegistry()返回的DefaultListableBeanFactory实例,DefaultListableBeanFactory实现了BeanDefinitionRegistry接口。
beanDefinitionMap是BeanDefinition实例最终注册到的地方
接下来继续分析registerBeanDefinitions:
// 加载并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
进入到registerBeanDefinitions方法
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
readerContext是经过包装的Resource对象,调用了doRegisterBeanDefinitions这个真正干活的do方法。
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
// BeanDefintion解析委托类
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 判断这个根节点是否是默认的命名空间,
// 底层就是判断这个根节点的nameSpaceUrl=="http://www.springframework.org/schema/beans"
if (this.delegate.isDefaultNamespace(root)) {
// 获取这个profile属性的值,表示剖面,用于设置环境
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
// 根据分隔符换换成数组
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
// 判断这个切面是否是激活的环境,如果不是直接返回,表示这个配置文件不是当前运行环境的配置文件
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 在解析xml之前做的准备工作,其实什么也没做
preProcessXml(root);
// 调用这个方法,解析
parseBeanDefinitions(root, this.delegate);
// 后续处理的
postProcessXml(root);
this.delegate = parent;
}
BeanDefinitionParserDelegate是个代理类,一会儿会委托这个对象来加载Document对象,this.delegate是具体的实例。
this.delegate.isDefaultNamespace(root)判断该xml是否是按照Spring定义的规范来编写的,如果是Spring定义的规则的话,就会尝试去获取profile标签:
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
profile主要用来区分项目的环境,可以用来区分线上或线下的环境(foodie的dev和product)
通过了上面的验证之后,doRegisterBeanDefinitions最后有两个钩子方法,方便去做定制化的事前和事后的处理,默认是没有任何实现。
preProcessXml(root);
postProcessXml(root);
两个钩子方法中间的是:
parseBeanDefinitions(root, this.delegate);
接收传进来的Document对象和传进来的BeanDefinitionParserDelegate对象
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;
// Bean定义的Document对象使用了Spring默认的XML命名空间
if (delegate.isDefaultNamespace(ele)) {
// 若是则按照spring原有的逻辑进行解析
parseDefaultElement(ele, delegate);
}
else {
// 否则使用扩展的自定义代理类进行解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 使用扩展的自定义代理类进行解析
delegate.parseCustomElement(root);
}
}
上面的代码会先判断Document是否符合Spring的规定,否则就有可能是用户自定义的另外一套的识别方案了。一般情况下是会进入到if里的,先从根部开始获取所有的子节点,并配合代理类对象delegate去解析,最终会调用parseDefaultElement。
在方法里会根据不同的标签去处理不同的逻辑
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果元素节点是<Import>导入元素,进行导入解析
// <import resource="classpath:applicationContext-datasource.xml"/>
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 如果元素节点是<Alias>别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
// 按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
// 按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
注册完毕后会发送一个事件进行通知。这里我们主要关注与bean标签相关的处理逻辑。processBeanDefinition(ele, delegate);
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
// 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 在完成BeanDefinition注册之后,往容器发送注册完成的事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这里终于开始调用delegate.parseBeanDefinitionElement(ele)来解析dom元素了,之所以层层调用,主要是考虑到了程序扩展性,中间有各种钩子和子类方法。
使用BeanDefinitionHolder来承接解析出来的BeanDefinition对象,本质上就是一个包装类
回到上面第4行调用的方法:
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
这里第二个参数传null表示bean标签没有嵌套的子bean标签。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取<Bean>元素中的id属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取<Bean>元素中的name属性值
// <bean class="study.spring.bean.Bean" id="bean1" name="firstBean 1stBean"></bean>
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
// 将<Bean>元素中的所有name属性值存放到别名中
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.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// 元素中是否包含嵌套<Bean>元素
if (containingBean == null) {
// 检查<Bean>元素所配置的id、name或者别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
// 对 <bean> 标签的其他属性进行解析
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 生要给BeanDefinition起名字
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.isTraceEnabled()) {
logger.trace("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;
}
该方法有很大一段逻辑是关于起名的,这里主要关注起名之后的parseBeanDefinitionElement方法
该方法返回一个AbstractBeanDefinition类型的实例,这就是我们一直要寻找的BeanDefinition实例了
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 如果<Bean>元素中配置了class属性,则获取class属性的值
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 如果<Bean>元素中配置了parent属性,则获取parent属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
// 为载入Bean定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
/**解析<bean>标签里的各种属性值,将其set进bd里面*/
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
方法里主要是解析bean标签的其他属性值。
进入到createBeanDefinition中,发现这里面创建出了BeanDefinition实例
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
进入到Util的createBeanDefinition中发现最终返回的是GenericBeanDefinition实例,从这里可以看到GenericBeanDefinition已经成为了Spring中主要的BeanDefinition
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
回到parseBeanDefinitionElement方法,剩下的内容主要就是对bean剩下的内容解析,解析完成之后会将相应的属性值set到BeanDefinition里。最后返回GenericBeanDefinition实例。
执行完毕后又回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法里
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
// 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 在完成BeanDefinition注册之后,往容器发送注册完成的事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
此时已经执行完了
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
之后还会看一下是否需要根据用户自定义的逻辑来处理额外的一些属性
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
这一步属于定制化扩展,关键的是
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
调用该方法会向容器里注册BeanDefinition,也就是往map中写入,注册完成后往容器发送注册完成的事件。
执行完毕之后又回到parseDefaultElement方法,当遇到<beans>标签后证明会有很多<bean>标签,然后执行doRegisterBeanDefinitions(ele);方法完成和上面相同的步骤。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果元素节点是<Import>导入元素,进行导入解析
// <import resource="classpath:applicationContext-datasource.xml"/>
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 如果元素节点是<Alias>别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
// 按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
// 按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}