Spring标签解析

2019/01/19 Spring

Spring标签解析

Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同

默认标签的解析

默认标签的解析是在parseDefaultElement函数中进行的,函数中的功能逻辑一目了然,分别对4种不同标签(import、alias、bean和beans)做了不同的处理。

    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);
         }
     //对beans标签的处理
         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
             doRegisterBeanDefinitions(ele);
         }
}

bean标签的解析及注册

在4种标签的解析中,对bean标签的解析最为复杂也最为重要,所以我们从此标签开始深入分析,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。首先我们进入函数processBeanDefinition(ele, delegate)。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
         if (bdHolder != null) {
             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
             try {
                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext(). getRegistry());
             }
             catch (BeanDefinitionStoreException ex) {
                 getReaderContext().error("Failed to register bean definition with name '" +
                         bdHolder.getBeanName() + "'", ele, ex);
             }
             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
         }
}

大致的逻辑总结如下。

  • 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class、name、id、alias之类的属性。
  • 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
  • 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了Bean- DefinitionReaderUtils的registerBeanDefinition方法。
  • 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。

解析BeanDefinition

首先我们从元素解析及信息提取开始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionDelegate类的parseBeanDefinitionElement方法。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
         return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition   
containingBean) {
     //解析id属性
         String id = ele.getAttribute(ID_ATTRIBUTE);
     //解析name属性    
     String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

     //分割name属性
         List<String> aliases = new ArrayList<String>();
         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 {
                     //如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的  
                          //beanName
                     if (containingBean != null) {
                         beanName = BeanDefinitionReaderUtils.generateBeanName(
                                 beanDefinition, this.readerContext.getRegistry(), true);
                     }
                     else {
                         beanName = this.readerContext.generateBeanName(beanDefinition);

                         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;
     }

以上便是对默认标签解析的全过程了。当然,对Spring的解析犹如洋葱剥皮一样,一层一层地进行,尽管现在只能看到对属性id以及name的解析,但是很庆幸,思路我们已经了解了。在开始对属性展开全面解析前,Spring在外层又做了一个当前层的功能架构,在当前层完成的主要工作包括如下内容。

  • 提取元素中的id以及name属性。
  • 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。
  • 如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName。
  • 将获取到的信息封装到BeanDefinitionHolder的实例中。

对标签其他属性的解析过程。

public AbstractBeanDefinition parseBeanDefinitionElement(
             Element ele, String beanName, BeanDefinition containingBean) {

         this.parseState.push(new BeanEntry(beanName));

         String className = null;
         //解析class属性
         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
         }

         try {
             String parent = null;
             //解析parent属性
             if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                 parent = ele.getAttribute(PARENT_ATTRIBUTE);
             }
             //创建用于承载属性的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;
}

接下来,我们继续一些复杂标签属性的解析。

创建用于属性承载的BeanDefinition

BeanDefinition是一个接口,在Spring中存在三种实现:RootBeanDefinition、ChildBean- Definition以及GenericBeanDefinition。三种实现均继承了AbstractBeanDefiniton,其中BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。

在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefiniton表示,而没有父就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象。

Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinition- Registry中读取配置信息。

由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例。而代码createBeanDefinition(className, parent)的作用就是实现此功能。

protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)  
 throws ClassNotFoundException {

         return BeanDefinitionReaderUtils.createBeanDefinition(
                 parentName, className, this.readerContext.getBeanClassLoader());
}

BeanDefinitionReaderUtils.java

public static AbstractBeanDefinition createBeanDefinition(
             String parentName, String className, ClassLoader classLoader) throws   
ClassNotFoundException {

         GenericBeanDefinition bd = new GenericBeanDefinition();
     //parentName可能为空        
     bd.setParentName(parentName);
         if (className != null) {
             if (classLoader != null) {
             //如果classLoader不为空,则使用以传入的classLoader同一虚拟机加载类对象,否则只是  
             //记录className
                 bd.setBeanClass(ClassUtils.forName(className, classLoader));
             }
             else {
                 bd.setBeanClassName(className);
             }
         }
         return bd;
}

解析各种属性

当我们创建了bean信息的承载实例后,便可以进行bean信息的各种属性解析了,首先我们进入parseBeanDefinitionAttributes方法。parseBeanDefinitionAttributes方法是对element所有元素属性进行解析:

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
             BeanDefinition containingBean, AbstractBeanDefinition bd) {

         //解析scope属性
         if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
             // Spring 2.x "scope" attribute
             bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
             if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
         //scope与singleton两个属性只能指定其中之一,不可以同时出现,否则Spring将会报出异常
                 error("Specify either 'scope' or 'singleton', not both", ele);
             }
         }
         //解析singleton属性
         else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
             // Spring 1.x "singleton" attribute
             bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
                     BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
         }
         else if (containingBean != null) {
         // Take default from containing bean in case of an inner bean definition.
         //在嵌入beanDifinition情况下且没有单独指定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));

         //解析dependency-check属性
         String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
         bd.setDependencyCheck(getDependencyCheck(dependencyCheck));

         //解析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);
             if (!"".equals(initMethodName)) {
                 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);
             if (!"".equals(destroyMethodName)) {
                 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;
     }

解析子元素meta

在开始解析元数据的分析前,我们先回顾一下元数据meta属性的使用。

<bean id="myTestBean" class="bean.MyTestBean">
         <meta key="testStr" value="aaaaaaaa"/>
</bean>

这段代码并不会体现在MyTestBean的属性当中,而是一个额外的声明,当需要使用里面的信息的时候可以通过BeanDefinition的getAttribute(key)方法进行获取。 对meta属性的解析代码如下:

     public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor   
attributeAccessor) {
         //获取当前节点的所有子元素
         NodeList nl = ele.getChildNodes();
         for (int i = 0; i < nl.getLength(); i++) {
             Node node = nl.item(i);
             //提取meta
             if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
                 Element metaElement = (Element) node;
                 String key = metaElement.getAttribute(KEY_ATTRIBUTE);
                 String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
                 //使用key、value构造BeanMetadataAttribute
                 BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
                 attribute.setSource(extractSource(metaElement));
                 //记录信息
                 attributeAccessor.addMetadataAttribute(attribute);
             }
         }
}

解析子元素lookup-method

子元素lookup-method似乎并不是很常用,但是在某些时候它的确是非常有用的属性,通常我们称它为获取器注入。引用Spring in Action中的一句话:获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里面配置的,此方法可用在设计有些可插拔的功能上,解除程序依赖

解析子元素replaced-method

这个方法主要是对bean中replaced-method子元素的提取,在开始提取分析之前我们还是预先介绍下这个元素的用法。

方法替换:可以在运行时用新的方法替换现有的方法。与之前的look-up不同的是,replaced-method不但可以动态地替换返回实体bean,而且还能动态地更改原有方法的逻辑。

解析子元素constructor-arg

对构造函数的解析是非常常用的,同时也是非常复杂的,也相信大家对构造函数的配置都不陌生,举个简单的小例子:

     <beans>  
    <!-- 默认的情况下是按照参数的顺序注入当指定index索引后就可以改变注入参数的顺序 -->  
    <bean id="helloBean" class="com.HelloBean">  
        <constructor-arg index="0">  
            <value>郝佳</value>  
        </constructor-arg>  
        <constructor-arg index="1">  
            <value>你好</value>  
        </constructor-arg>  
    </bean>  
... ...
    </beans>

上面的配置是Spring构造函数配置中最基础的配置,实现的功能就是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。那么让我们来看看具体的XML解析过程。 对于constructor-arg子元素的解析,Spring是通过parseConstructorArgElements函数来实现的,具体的代码如下:

    public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
         NodeList nl = beanEle.getChildNodes();
         for (int i = 0; i < nl.getLength(); i++) {
             Node node = nl.item(i);
             if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
                 //解析constructor-arg
                 parseConstructorArgElement((Element) node, bd);
             }
         }
}

解析子元素property

parsePropertyElement函数完成了对property属性的提取,property使用方式如下:

<bean id="test" class="test.TestClass">
     <property name="testStr" value="aaa"/>
</bean>

而具体的解析过程如下:

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
         NodeList nl = beanEle.getChildNodes();
         for (int i = 0; i < nl.getLength(); i++) {
             Node node = nl.item(i);
             if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                 parsePropertyElement((Element) node, bd);
             }
         }
}

解析子元素qualifier

对于qualifier元素的获取,我们接触更多的是注解的形式,在使用Spring框架中进行自动注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个。当找不到一个匹配的Bean时,Spring容器将抛出BeanCreationException异常,并指出必须至少拥有一个匹配的Bean。

<bean id="myTestBean" class="bean.MyTestBean">
    <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf"/>
</bean>

AbstractBeanDefinition属性

至此我们便完成了对XML文档到GenericBeanDefinition的转换,也就是说到这里,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的配置。

GenericBeanDefinition只是子类实现,而大部分的通用属性都保存在了AbstractBeanDefinition中,那么我们再次通过AbstractBeanDefinition的属性来回顾一下我们都解析了哪些对应的配置。

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
         implements BeanDefinition, Cloneable {

//此处省略静态变量以及final常量

     /**
      * bean的作用范围,对应bean属性scope
      */
     private String scope = SCOPE_DEFAULT;

     /**
      * 是否是单例,来自bean属性scope
      */
     private boolean singleton = true;

     /**
      * 是否是原型,来自bean属性scope
      */
     private boolean prototype = false;

     /**
      * 是否是抽象,对应bean属性abstract
      */
     private boolean abstractFlag = false;

     /**
      * 是否延迟加载,对应bean属性lazy-init
      */
     private boolean lazyInit = false;

     /**
      * 自动注入模式,对应bean属性autowire
      */
     private int autowireMode = AUTOWIRE_NO;

     /**
      * 依赖检查,Spring 3.0后弃用这个属性
      */
     private int dependencyCheck = DEPENDENCY_CHECK_NONE;
     /**
      * 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on
      */
     private String[] dependsOn;

     /**
      * autowire-candidate属性设置为false,这样容器在查找自动装配对象时,
      * 将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他bean的。 
      *  对应bean属性autowire-candidate
      */
     private boolean autowireCandidate = true;

     /**
      * 自动装配时当出现多个bean候选者时,将作为首选者,对应bean属性primary
      */
     private boolean primary = false;

     /**
      * 用于记录Qualifier,对应子元素qualifier
      */
     private final Map<String, AutowireCandidateQualifier> qualifiers =
             new LinkedHashMap<String, AutowireCandidateQualifier>(0);

     /**
      * 允许访问非公开的构造器和方法,程序设置
      */
     private boolean nonPublicAccessAllowed = true;

     /**
      * 是否以一种宽松的模式解析构造函数,默认为true,
      * 如果为false,则在如下情况
      * interface ITest{}
      * class  ITestImpl implements ITest{};
      * class Main{
      *     Main(ITest i){}
      *     Main(ITestImpl i){}
      * }
      * 抛出异常,因为Spring无法准确定位哪个构造函数
      * 程序设置
      */
     private boolean lenientConstructorResolution = true;

     /**
      * 记录构造函数注入属性,对应bean属性constructor-arg
      */
     private ConstructorArgumentValues constructorArgumentValues;

     /**
      * 普通属性集合
      */
     private MutablePropertyValues propertyValues;

     /**
      * 方法重写的持有者 ,记录lookup-method、replaced-method元素
      */
     private MethodOverrides methodOverrides = new MethodOverrides();

     /**
      * 对应bean属性factory-bean,用法:
      * <bean id="instanceFactoryBean" class="example.chapter3.InstanceFactoryBean"/>
     * <bean id="currentTime" factory-bean="instanceFactoryBean" factory-method="   
createTime"/>

      */
     private String factoryBeanName;

     /**
      * 对应bean属性factory-method
      */
     private String factoryMethodName;

     /**
      * 初始化方法,对应bean属性init-method
      */
     private String initMethodName;

     /**
      * 销毁方法,对应bean属性destory-method
      */
     private String destroyMethodName;

     /**
      * 是否执行init-method,程序设置
      */
     private boolean enforceInitMethod = true;

     /**
      * 是否执行destory-method,程序设置
      */
     private boolean enforceDestroyMethod = true;

     /**
      * 是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置
      */
     private boolean synthetic = false;

     /**
      * 定义这个bean的应用 ,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关,SUPPORT:某些复杂配置的一部分
      * 程序设置
      */
     private int role = BeanDefinition.ROLE_APPLICATION;

     /**
      * bean的描述信息
      */
     private String description;
     /**
      * 这个bean定义的资源
      */
     private Resource resource;

//此处省略set/get方法
}

解析默认标签中的自定义标签元素

再次回顾下默认标签解析函数的起始函数:

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, getReader   
Context().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));
         }
}

分析源码得知这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope时默认使用父类的属性,这里分析的是顶层配置,所以传递null。将第三个参数设置为空后进一步跟踪函数:

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
     Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd){

             BeanDefinitionHolder finalDefinition = definitionHolder;

             NamedNodeMap attributes = ele.getAttributes();
         //遍历所有的属性,看看是否有适用于修饰的属性
             for (int i = 0; i < attributes.getLength(); i++) {
                 Node node = attributes.item(i);
                 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
             }

         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;
}

我们继续跟踪代码:

private BeanDefinitionHolder decorateIfRequired(
         Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {

     //获取自定义标签的命名空间
         String namespaceUri = getNamespaceURI(node);
     //对于非默认标签进行修饰
         if (!isDefaultNamespace(namespaceUri)) {
     //根据命名空间找到对应的处理器
             NamespaceHandler handler=this.readerContext. getNamespaceHandler Resolver(). resolve(namespaceUri);
             if (handler != null) {
     //进行修饰
                 return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
             }
             else if (namespaceUri != null && namespaceUri.startsWith("http://www.  
 springframework.org/")) {
                 error("Unable to locate Spring NamespaceHandler for XML schema   
namespace [" + namespaceUri + "]", node);
             }
             else {
                 // A custom namespace, not to be handled by Spring - maybe "xml:...".
                 if (logger.isDebugEnabled()) {
                     logger.debug("No Spring NamespaceHandler found for XML schema   
namespace [" + namespaceUri + "]");
                 }
             }
         }
         return originalDef;
}

public String getNamespaceURI(Node node) {
         return node.getNamespaceURI();
}

public boolean isDefaultNamespace(String namespaceUri) {
         //BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
         return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals   
(namespaceUri));
}

注册解析的BeanDefinition

对于配置文件,解析也解析完了,装饰也装饰完了,对于得到的beanDinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())代码的解析了。

public static void registerBeanDefinition(
             BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
             throws BeanDefinitionStoreException {

         //使用beanName做唯一标识注册
         String beanName = definitionHolder.getBeanName();
         registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

     //注册所有的别名
         String[] aliases = definitionHolder.getAliases();
         if (aliases != null) {
             for (String aliase : aliases) {
                 registry.registerAlias(beanName, aliase);
             }
         }
     }

从上面的代码可以看出,解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而对于beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。

通过beanName注册BeanDefinition

对于beanDefinition的注册,或许很多人认为的方式就是将beanDefinition直接放入map中就好了,使用beanName作为key。确实,Spring就是这么做的,只不过除此之外,它还做了点别的事情。

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 {
                 /*
                  * 注册前的最后一次校验,这里的校验不同于之前的XML文件校验,
                  * 主要是对于AbstractBeanDefinition属性中的methodOverrides校验,
                  * 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
                  */
                 ((AbstractBeanDefinition) beanDefinition).validate();
             }
             catch (BeanDefinitionValidationException ex) {
                 throw new BeanDefinitionStoreException (beanDefinition. getResource   
Description(), beanName,
                         "Validation of bean definition failed", ex);
             }
         }
         //因为beanDefinitionMap是全局变量,这里定会存在并发访问的情况
         synchronized (this.beanDefinitionMap) {
             Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
             //处理注册已经注册的beanName情况
             if (oldBeanDefinition != null) {
                 //如果对应的BeanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常
                 if (!this.allowBeanDefinitionOverriding) {
                     throw new BeanDefinitionStoreException(beanDefinition. getResource   
Description(), beanName,
                             "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                             "': There is already [" + oldBeanDefinition + "] bound.");
                 }else {
                     if (this.logger.isInfoEnabled()) {
                         this.logger.info("Overriding bean definition for bean '" + beanName +
                                 "': replacing [" + oldBeanDefinition + "] with [" +   
beanDefinition + "]");
                     }
                 }
             }else {
                 //记录beanName
                 this.beanDefinitionNames.add(beanName);
                 this.frozenBeanDefinitionNames = null;
             }
             //注册beanDefinition
             this.beanDefinitionMap.put(beanName, beanDefinition);
         }
         //重置所有beanName对应的缓存
         resetBeanDefinition(beanName);
}

对于bean的注册处理方式上,主要进行了几个步骤。

  • 对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验时针对于XML格式的校验,而此时的校验时针是对于AbstractBean- Definition的methodOverrides属性的。
  • 对beanName已经注册的情况的处理。如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖。
  • 加入map缓存。
  • 清除解析之前留下的对应beanName的缓存。
  • 通过别名注册BeanDefinition

Search

    微信好友

    博士的沙漏

    Table of Contents