以代码流程方式探索Spring源码--默认BeanDefinition的解析
Spring中默认标签的解析分为三种:bean标签、import标签、alias标签。其中最核心的就是bean标签的解析,也是我们的重点分析目标,整明白bean标签,其他标签也就手到擒来。
Parse解析前的准备工作
类:DefaultBeanDefinitonDocumentReader
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import标签解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias标签解析 别名标签
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean标签,这个方法正是我们的入口
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//在这个方法中解析document,封装成BeanDefinition对象
//spring中使用的这种方式,将核心代码封装进一个方法,在前后分别有前置处理和后置处理,我们可以学习下
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//该方法功能不重要,设计模式重点看一下,装饰者设计模式,加上SPI设计思想
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
省略异常捕获...
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
将parse任务交给ParseDelegate去执行(实际的创建逻辑)
类:BeanDefinitionParseDelegate
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//首先获取<bean>标签中的id属性和name属性的值,有了这两个属性就能得到贯穿流程的beanName
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//获取别名,即alias属性
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;
省略对beanName的一点判断操作(如果没有beanName,取alias的第一个作为beanName)
//检查beanName是否重复
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//又是一个重载方法,spring就是用这样一个个重载方法把我们整晕的,但这个是核心,还不能晕
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
省略对beanName万一不存在的操作,简单来说就是根据类自动生成beanName
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//把解析到的beanDefinition,beanName和aliasesArray包装为bdHolder对象
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
//获取<bean>标签中的class属性
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 {
//先获取Class信息和父类信息,这样就可以创建GenericBeanDefinition对象了
//跟进去就会发现是使用工具类来创建对象,对象的实际类型是GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析bean标签内部的属性,并把解析出来的属性设置到BeanDefinition对象中
//scope、abstract、lazy-init、autowire、depends-on、autowire-candidate、primary、
//init-method、destroy-method、factory-method、factory-bean
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析bean中的meta标签
//获取key和value并封装为BeanMetadataAttribute对象设置进bd中
parseMetaElements(ele, bd);
//解析bean中的lookup-method标签
//解析look-up标签的name属性和bean属性并封装为LookupOverride放进bd的MethodOverrides中
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析bean中的replaced-method标签
//解析replace-method标签的name属性和replacer属性并封装成ReplaceOverride放进bd的MethodOverrides中
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析bean中的constructor-arg标签
parseConstructorArgElements(ele, bd);
//解析bean中的property标签
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
省略异常捕获
finally {
this.parseState.pop();
}
return null;
}
创建BeanDefinition的后续处理–装饰
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
下面这个方法就是解析一下我们的默认标签中使用到的自定义的标签,现在用的很少了。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
//根据bean标签属性装饰BeanDefinitionHolder,比如<bean class="xx" p:username="jack"/>
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
//根据bean标签子元素装饰BeanDefinitionHolder
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
这里与解自定义标签的过程其实一样,都是通过命名空间拿到处理类后进行处理。
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
//根据node获取到node的命名空间,形如:http://www.springframework.org/schema/p p:username="Jack"
String namespaceUri = getNamespaceURI(node);
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
//这里有SPI服务发现的思想,根据配置文件获取namespaceUri对应的处理类
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
//调用NamespaceHandler处理类的decorate方法,开始具体装饰过程,并返回装饰完的对象
//org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
//省略非正常路径的代码
return originalDef;
}
BeanDefinition的缓存注册
//完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
//完成BeanDefinition的注册
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//建立别名和 id的映射,这样就可以根据别名获取到id
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
核心逻辑如下:主要就是把beanName和beanDefinition放进相应的map中。
//把beanDefinition缓存到map中
this.beanDefinitionMap.put(beanName, beanDefinition);
//把beanName放到beanDefinitionNames list中,这个list着重记住,bean实例化的时候需要用到
this.beanDefinitionNames.add(beanName);
发送注册时间
这个部门还没有认真看,有需要可以挖掘下
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
小结
根据不同的标签类型进入到不同的默认标签解析方法
===>由BeanDefinitionParserDelegate对象进行解析
===>先收集id属性和name属性生成beanName
===>收集class属性值和parent属性值新建GenericBeanDefinition对象
===>解析bean标签内部的属性值放进bd
===>解析bean标签的子标签内容存放进bd
===>设置Resource和Source,资源和来源
===>检查有没有beanName,没有的话使用自动生成方法生成beanName
===>封装为bdHolder
===>有需要的话装饰bdHolder,也就是解析默认标签中使用的自定义标签
===>缓存注册bdHolder,也就是存进beanFactory中对应的map中,name和definition各有一个map
===>发送完成bean注册的消息