bean标签的解析
前言
之前的文章中提到过Spring中的标签包括默认和自定义两种标签,而两种标签的解析以及用法存在着很大的不同,接下来的文章将讲解默认标签的import、alias、bean、beans。这篇文章讲述的是bean标签。
默认标签
默认的标签实在parseDefaultElement 函数中进行解析的,函数中的逻辑很清楚,分别对4种不同的标签做了不同的处理,如下:
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 //对import标签的处理 3 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 4 importBeanDefinitionResource(ele); 5 } 6 //对alias标签的处理 7 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 8 processAliasRegistration(ele); 9 } 10 //对bean标签的处理 11 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 12 processBeanDefinition(ele, delegate); 13 } 14 //对beans标签的处理 15 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { 16 // recurse 17 doRegisterBeanDefinitions(ele); 18 } 19 }
bean标签的解析及注册
在4种标签中,bean标签的解析最重要而且最复杂,所以我们先从此标签开始深入分析,如果能理解此标签的解析过程,那么其他的标签也将会迎刃而解。首先我们来看看processBeanDefinition(ele,delegate)方法,对bean标签的解析进行一个大致上的流程了解:
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 2 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 3 if (bdHolder != null) { 4 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 5 try { 6 // Register the final decorated instance. 7 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 8 } 9 catch (BeanDefinitionStoreException ex) { 10 getReaderContext().error("Failed to register bean definition with name '" + 11 bdHolder.getBeanName() + "'", ele, ex); 12 } 13 // Send registration event. 14 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 15 } 16 }
对这段代码解释一下:
(1)第2行:首先委托BeanDefinitionDelegate类的 parseBeanDefinitionElement()方法进行元素解析,返回BeanDefinitionHolder类的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置的各种属性了,例如:class、name、id、alias之类的属性。
(2)第4行:当返回的bdHolder不为空的情况下,若存在默认标签的子节点下还有自定义的属性,还需要再次对自定义标签进行解析。
(3)第7行:解析完成后,需要对解析后的bdHolder进行注册,同样,注册委托给了BeanDefinitionReaderUtils的registerBeanDefinition()方法。
(4)第14行:发出响应事件,通知相关的监听器,这个bean已经加载完成了。
下面是bean标签解析的时序图,来加深理解:
解析BeanDefinition
上面我们对于bean标签的解析有一个大致的了解,接下来我们针对各个操作做具体的分析,首先我们从元素的解析及信息提取开始,也就是:
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
进入BeanDefinitionDelegate类的parseBeanDefinitionElement()方法:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); }
1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { 2 //解析id属性 3 String id = ele.getAttribute(ID_ATTRIBUTE); 4 //解析name属性 5 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 6 //分割name属性 7 List<String> aliases = new ArrayList<>(); 8 if (StringUtils.hasLength(nameAttr)) { 9 // 将name属性的值通过,; 进行分割 转为字符串数字(即在配置文件中如配置多个name 在此做处理) 10 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 11 aliases.addAll(Arrays.asList(nameArr)); 12 } 13 14 String beanName = id; 15 // 如果ID为空 使用配置的第一个name属性作为ID 16 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 17 beanName = aliases.remove(0); 18 if (logger.isTraceEnabled()) { 19 logger.trace("No XML 'id' specified - using '" + beanName + 20 "' as bean name and " + aliases + " as aliases"); 21 } 22 } 23 24 if (containingBean == null) { 25 // 校验beanName和aliases的唯一性 26 // 内部核心为使用usedNames集合保存所有已经使用了的beanName和alisa 27 checkNameUniqueness(beanName, aliases, ele); 28 } 29 30 // 进一步解析其他所有属性到GenericBeanDefinition对象中 31 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 32 if (beanDefinition != null) { 33 //如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName 34 if (!StringUtils.hasText(beanName)) { 35 try { 36 if (containingBean != null) { 37 beanName = BeanDefinitionReaderUtils.generateBeanName( 38 beanDefinition, this.readerContext.getRegistry(), true); 39 } 40 else { 41 beanName = this.readerContext.generateBeanName(beanDefinition); 42 // Register an alias for the plain bean class name, if still possible, 43 // if the generator returned the class name plus a suffix. 44 // This is expected for Spring 1.2/2.0 backwards compatibility. 45 String beanClassName = beanDefinition.getBeanClassName(); 46 if (beanClassName != null && 47 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 48 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 49 aliases.add(beanClassName); 50 } 51 } 52 if (logger.isTraceEnabled()) { 53 logger.trace("Neither XML 'id' nor 'name' specified - " + 54 "using generated bean name [" + beanName + "]"); 55 } 56 } 57 catch (Exception ex) { 58 error(ex.getMessage(), ele); 59 return null; 60 } 61 } 62 String[] aliasesArray = StringUtils.toStringArray(aliases); 63 // 将信息封装到BeanDefinitionHolder对象中 64 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 65 } 66 67 return null; 68 }
上述代码便是对默认标签解析的全过程了。当然了,对Spring的解析需要像洋葱剥皮一样,一层一层的进行。尽管现在只能看到对id以及name属性的解析,但是思路我们已经了解了,在对属性展开解析前,Spring在外层又做了一个当前层的功能架构,在当前层完成的主要工作如下:
(1)第3~28行:提取元素中的id以及name属性。
(2)第31行:进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。
(3)第32~61行:如果检测到Bean没有指定beanName,那么使用默认规则为此Bean生成beanName。
(4)第64行:将获取到的信息封装到BeanDefinitionHolder的实例中。
来进一步查看步骤2,中对标签其他属性的解析过程:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; //解析class属性 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; //解析parent属性 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)); //解析元数据 parseMetaElements(ele, bd); //解析lookup-Method属性 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析replaced-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; }
上述代码解析了bean标签的所有属性,不论是常用的还是不常用的,但是还是有一些复杂的属性需要进一步的解析,接下来,我们继续了解这些复杂的标签属性的解析:
创建用于属性承载的BeanDefinition
BeanDefinition是一个接口,在Spring中有三个对它的实现:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。这三种实现均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式。<bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和<bean>中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的<bean>元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。
在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefinition表示,而没有父<bean>的<bean>就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象。
Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。它们之间的关系如下:
由上图可以看出,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,而上述代码createBeanDefinition(className,parent)的作用就是实现此功能。
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); }
跟踪代码下去:BeanDefinitionReaderUtils类:
public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); // parentName可能为空 bd.setParentName(parentName); if (className != null) { // 如果classLoader不为空 // 则使用传入的classLoader同一虚拟机加载类对象 否则只记录classLoader if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; }
至此 createBeanDefinition() 已经说完了,而且我们也获得了用于承载属性的AbstractBeanDefinition了。
解析各种属性
上述已经了解了创建bean信息的承载实例,接下来便可以进行bean信息的各种属性解析了,首先我们进入parseBeanDefinitionAttributes方法,此方法是对Element所有元素属性进行解析:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { //scope和singleton两种属性只能指定其中之一,不可以同时出现,否则Spring会抛出异常 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } //解析scope属性 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null) { // 在嵌入BeanDefinition情况下且没有单独指定scope属性则使用父类默认的属性 bd.setScope(containingBean.getScope()); } //解析abstract属性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } //解析lazy-init属性 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } //若没有设置或者设置成其他字符都会被设置为false bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); //解析autowire属性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); //解析depends-on属性 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } //解析autowire-candidate属性 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } //解析primary属性 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } //解析init-method属性 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } else if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } //解析destroy-method属性 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } else if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } //解析factory-method属性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } //解析factory-bean属性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
从上述代码中可以清楚的看出Spring完成了对所有bean属性的解析,这些属性中有很多是我们经常使用到的,当然也有一些是不熟悉甚至都没有使用过的,有兴趣的可以继续追踪源码了解每一个属性。
在完成上一步,也就是对bean所有属性的解析后,将会把获取到的信息都封装到BeanDefinitionHolder中,来看看这个类的构造函数的源码:
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; }
可以看出也就跟普通的构造函数一样,对相应的属性赋值,构造一个BeanDefinitionHolder的实例。
以上,就是对于bean标签的解析。
参考:《Spring源码深度解析》 郝佳 编著:
posted on 2018-12-06 21:48 AoTuDeMan 阅读(1125) 评论(0) 编辑 收藏 举报