【Spring源码解析】Spring XML配置默认标签解析
1、前言
上文对XmlBeanFactory文件资源加载的流程做了初步的讲解,主要内容是Reousce资源的加载,XmlBeanFactory初始化,Resouce对象进行编码并转换成数据流,Xml配置文件验证,转换成Document对象,之后进行解析注册操作。
上次解析的内容是初步整理,到 registerBeanDefinitions 方法,此处继续向下解析。
2、从解析到注册
// 执行解析注册前的前置处理
preProcessXml(root);
// 注册解析
parseBeanDefinitions(root, this.delegate);
// 执行注册解析后的后置处理
postProcessXml(root);
preProcessXml,postProcessXml 都是给使用的时候扩展使用,Spring 并没有完成功能。
parseBeanDefinitions是主要的解析内容:
2.1、解析主要步骤
解析的过程主要分为默认标签的解析,就是Spring 自带标签的解析,以及扩展和自定义标签的解析。
delegate.isDefaultNamespace(root) 就是用来判断是否为默认标签。
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);
}
}
- isDefaultNamespace
public boolean isDefaultNamespace(Node node) {
return isDefaultNamespace(getNamespaceURI(node));
}
通过断点调试可以发现,这里 getNamespaceURI 判断相等 http://www.springframework.org/schema/beans 也就是 Xml 的根节点 xmlns 的属性值,也就是从根节点开始解析获取所有的子节点,如果不是根节点,那么就采用自定义的节点解析。
之后获取所有的子节点,并判断所有的子节点是否为默认的标签元素,如果是采用默认标签的解析方式,如果不是采用自定义的解析方式进行解析。
- 默认标签解析 parseDefaultElement
- 自定义标签的解析 delegate.parseCustomElement
2.2、默认标签解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
通过查看IMPORT_ELEMENT,ALIAS_ELEMENT,BEAN_ELEMENT,NESTED_BEANS_ELEMENT四个常量的值 我们可以看到分别对应标签<import>,<alias>,<bean>和<beans>。 所以spring 根节点下有这四种默认的标签。
而这四种标签中bean标签的内容是最多的,也是最复杂的,其他三种都是对bean标签功能的补充和扩展。
我们这里就解析bean标签的内容。
2.3、bean标签
processBeanDefinition 方法是对bean标签的解析,点击查看:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 对标签进行解析,节点对象转换
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 解析bean标签下的自定义标签内容
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));
}
}
我们可以得到注册的四个步骤:
- 通过bean标签的解析并转换成节点对象
- 对bean标签自定义标签的解析
- 把解析后的节点对象注册到缓存中
- 把注册完成的事务发送给监听器
2.4、标签转换成节点对象
@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));
}
// 假如name属性和id属性相同,那么移除相同的别名
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");
}
}
// 检查id 和别名,并放入Set集合 usedNames
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 创建BeanDefination ,进一步解析其他标签
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果beanName不存在那么就创建一个默认的
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);
// 返回bean持有对象
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
通过以上注释,我们可以看到bean标签的解析共有几个步骤:
- 获取id和name属性的值,并把name属性作为别名,并处理id和name属性相同的情况
- 检查id和name属性值是否以及被其他bean使用
- 创建bean标签对应的对象BeanDefination,并进一步解析其他标签
- 如果察觉beanName也就是id属性不存在,那么就根据默认规则创建
- 把BeanDefination,beanName,aliasArray封装成对象返回
其中除了3之外其他的都比较简单,这里继续查看parseBeanDefinitionElement方法。
2.5、解析其他标签
parseBeanDefinitionElement 方法进入,这里面的内容就比较多:
@Nullable
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 {
// 创建对象 AbstractBeanDefinition 承载数GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬编码解析bean的各个属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 解析 Description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元数据meta
parseMetaElements(ele, bd);
// 解析lookup-method标签
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replace-method
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造方法
parseConstructorArgElements(ele, bd);
// 解析property
parsePropertyElements(ele, bd);
// 解析qualifier
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;
}
从这里我们可以看到许多常用的标签的解析情况。
解析的整个步骤为:
1. 先放入parseState 的双端队列
2. 解析class,parent 标签
3. 创建对象 AbstractBeanDefinition 承载数GenericBeanDefinition
4. 解析bean的各个属性,这里比较多下面介绍
5. 解析子标签description
6. 解析子标签meta
7. 解析子标签lookup-method
8. 解析子标签replace-method
9. 解析子标签constructor-arg
10. 解析子标签property
11. 解析子标签qualifier
12. 设置Resouce,source
13. 从双端队列弹出
- createBeanDefinition
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader());
}
进入BeanDefinitionReaderUtils 查看,可以得知真正创建的是AbstractBeanDefinition的实现类GenericBeanDefinition,并setParentName,根据className来判断setBeanClass或者setBeanClassName。
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;
}
- 解析bean的各个属性parseBeanDefinitionAttributes
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd)
这里可以看到解析了 singleton,scope,abstract,lazy-init,autowire,depends-on,autowire-candidate,primary,init-method,destroy-method,factory-method,factory-bean。
以上就是bean标签的所有属性。解析的时候大多是简单处理放入AbstractBeanDefinition 对象,这里只处理主题流程,稍后单独写进行解析。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// singleton
if (ele.hasAttribute("singleton")) {
this.error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
// scope
} else if (ele.hasAttribute("scope")) {
bd.setScope(ele.getAttribute("scope"));
} else if (containingBean != null) {
bd.setScope(containingBean.getScope());
}
// abstract
if (ele.hasAttribute("abstract")) {
bd.setAbstract("true".equals(ele.getAttribute("abstract")));
}
// lazy-init
String lazyInit = ele.getAttribute("lazy-init");
if (this.isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit("true".equals(lazyInit));
// autowire
String autowire = ele.getAttribute("autowire");
bd.setAutowireMode(this.getAutowireMode(autowire));
String autowireCandidate;
// depends-on
if (ele.hasAttribute("depends-on")) {
autowireCandidate = ele.getAttribute("depends-on");
bd.setDependsOn(StringUtils.tokenizeToStringArray(autowireCandidate, ",; "));
}
// autowire-candidate
autowireCandidate = ele.getAttribute("autowire-candidate");
String destroyMethodName;
if (this.isDefaultValue(autowireCandidate)) {
destroyMethodName = this.defaults.getAutowireCandidates();
if (destroyMethodName != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(destroyMethodName);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
} else {
bd.setAutowireCandidate("true".equals(autowireCandidate));
}
// primary
if (ele.hasAttribute("primary")) {
bd.setPrimary("true".equals(ele.getAttribute("primary")));
}
// init-method
if (ele.hasAttribute("init-method")) {
destroyMethodName = ele.getAttribute("init-method");
bd.setInitMethodName(destroyMethodName);
} else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// destroy-method
if (ele.hasAttribute("destroy-method")) {
destroyMethodName = ele.getAttribute("destroy-method");
bd.setDestroyMethodName(destroyMethodName);
} else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// factory-method
if (ele.hasAttribute("factory-method")) {
bd.setFactoryMethodName(ele.getAttribute("factory-method"));
}
// factory-bean
if (ele.hasAttribute("factory-bean")) {
bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
}
return bd;
}
- 子元素解析
此处为IDEA提示所有子元素:
完成属性和子元素解析后就对bean所有的内容解析完毕,之后再设置Resouce 和 source,最后弹出
parseState 完成默认标签的解析。
2.6、自定义属性解析
默认的属性和子标签解析完成,可能还会存在自定义的标签,这里对这类标签的解析内容。
例如:<bean id = “stu” class = “com.fans.Student”><my:age>12<my:age></bean>
其中my:age就是自定义标签。
delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)
进入方法内部查看:
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
return this.decorateBeanDefinitionIfRequired(ele, definitionHolder, (BeanDefinition)null);
}
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = definitionHolder;
NamedNodeMap attributes = ele.getAttributes();
for(int i = 0; i < attributes.getLength(); ++i) {
Node node = attributes.item(i);
finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
}
NodeList children = ele.getChildNodes();
for(int i = 0; i < children.getLength(); ++i) {
Node node = children.item(i);
if (node.getNodeType() == 1) {
finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
通过以上代码,我们可以了解到:
- 首先获取所有的属性: NamedNodeMap attributes = ele.getAttributes();
- 装饰所有的属性:finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
- 然后获取所有的子标签: NodeList children = ele.getChildNodes();
- 装饰所有的子标签:finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
这里的处理顺序其实是和默认标签相同。
- decorateIfRequired 查看装饰的过程:
public BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
String namespaceUri = this.getNamespaceURI(node);
if (namespaceUri != null && !this.isDefaultNamespace(namespaceUri)) {
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
} else if (namespaceUri.startsWith("http://www.springframework.org/")) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
return originalDef;
}
装饰的过程其实也是和默认标签相同:
- 获取命名空间URI,String namespaceUri = this.getNamespaceURI(node);
- 根据命名空间找到相应的处理器,NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)
- 解析自定义内容进行修饰,BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd))
2.7、对bean进行注册
进行注册的过程实质上是放入缓存。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
进入方法内部:
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) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
registry.registerAlias(beanName, alias);
}
}
}
解析上述代码:
- 先获取beanName
- 执行beanName注册放入缓存
- 处理别名注册
- 执行beanName注册
再具体一点,执行beanName注册,进入registerBeanDefinition方法,这里接口有多种实现,通过断点可知是DefaultListableBeanFactory。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 1.对beanDefination 进行校验,校验methodOverrides的方法是否在工厂中存在
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var8) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);
}
}
// 2.判断beanName已注册完成
BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 3. 判断是否可以重写
if (!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// ... 省略部分日志代码
// 4. 执行写入beanDefinitionMap缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
if (this.hasBeanCreationStarted()) {
synchronized(this.beanDefinitionMap) {
// 5. bean 放入缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
// 6. beanName 放入缓存
this.beanDefinitionNames = updatedDefinitions;
// 7. 清理缓存
this.removeManualSingletonName(beanName);
}
} else {
// 5. bean 放入缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
// 6. beanName 放入缓存
this.beanDefinitionNames.add(beanName);
// 7. 清理缓存
this.removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || this.containsSingleton(beanName)) {
this.resetBeanDefinition(beanName);
}
}
以上注册分为上述7步骤:
- 对beanDefination 进行校验,校验methodOverrides的方法是否在工厂中存在,这里涉及lookup-method 和replace-method 属性,有兴趣的可以查看下。
- 判断beanName已注册完成,从缓存中获取
- 如果已经存在,判断是否可以重写,如果可以执行重写,如果不可以抛出异常
- 执行写入beanDefinitionMap缓存,也就是执行重写的步骤
- 针对是否已存在创建对象,对缓存写入进行锁住,作用是相同的,先执行放入缓存
- 完成之后放入beanName
- 最后执行清理manualSingletonNames中的beanName缓存
- 执行别名注册
这里别名注册就比较简单,如果存在同名的就移除这个别名,不同名的话,找到真实注册名,判断是否与传承name相等,并处理别名循环依赖的问题。
最后执行放入别名缓存。
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized(this.aliasMap) {
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
} else {
String registeredName = (String)this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
return;
}
if (!this.allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'");
}
}
this.checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
以上就完成了注册的所有操作,最后就是通知监听,spring 没有具体的监听处理内容,如果需要可以完成监听内容。
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
3、总结
上面就完成了默认bean标签解析的所有内容。下篇会说一下其他三种标签的解析过程,并和bean标签进行对比。
再次梳理的过程又有收获。希望你也是,加油,共勉!