Spring IOC 标签的解析

Posted wcj-java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring IOC 标签的解析相关的知识,希望对你有一定的参考价值。

Spring IOC 标签的解析

上一篇文章说了Spring中的标签包括默认标签和自定义标签两种,本节继续来研究默认标签的解析,parseBeanDefinitions方法为解析标签的总入口

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

 默认标签的解析是在parseBeanDefinitions函数中进行的,该函数对4种不同的标签做处理   

 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
           // 对import标签的处理
           importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
           // 对alias标签的处理
           processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
           // 对bean标签的处理
           processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
           // 对beans标签的处理
           doRegisterBeanDefinitions(ele);
        }
  }

 4种标签的处理中,对bean标签的解析最为复杂也最为重要,所以以此标签分析为例。

1.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
 
    <bean id="user" class="com.example.demo.entity.User">
        <property name="name" value="zhangsanfeng"></property>
        <property name="age" value="14"></property>
    </bean>
</beans>

 首先进入函数processBeanDefinition(ele, delegate)   

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

 此方法大致的逻辑如下

(1)     首先委托BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法进行元素的解析,返回BeanDefinitionHolder类型的实例bdHolder,这个实例bdHolder包含我们配置文件中配置的各种属性了。

(2)     当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。

(3)     解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法。

(4)     最后发出响应时间,通知相关的监听器,这个bean已经加载完成了。

2.解析BeanDefinition

进入BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法   

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

   

 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));
        }
        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 {
                   // 如果不存在beanNamename根据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;
    }

 该函数主要功能包括如下内容:

(1)     提取元素中的id以及name属性。

(2)     进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。

(3)     如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName。

(4)     将获取到的信息封装到BeanDefinitionHolder的实例中。

Spring的解析犹如洋葱剥皮一样,一层一层的进行。接下来看步骤(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标签的所有属性都做了处理,继续对属性的解析。

2.1创建用于属性承载的BeanDefinition

BeanDefinition是一个接口,在Spring中存在三种实现:RootBeanDefinitionChildBeanDefinition、GenericBeanDefinition,三种实现均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式,BeanDefinition和<bean>中的属性是一一对应的。

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

创建用于承载属性的实例在createBeanDefinition方法中   

 protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
           throws ClassNotFoundException {
 
        return BeanDefinitionReaderUtils.createBeanDefinition(
               parentName, className, this.readerContext.getBeanClassLoader());
}

     

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) {
           if (classLoader != null) {
               // 如果classLoader不为空,则使用已传入的classLoader统一虚拟机加载类对象,否则只记录className
               bd.setBeanClass(ClassUtils.forName(className, classLoader));
           }
           else {
               bd.setBeanClassName(className);
           }
        }
        return bd;
}

 2.2解析各种属性

创建了bean信息的承载实例后,便可以进行bean信息的各种属性解析了,进入parseBeanDefinitionAttributes方法   

 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
           @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
 
        if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
           error("Old 1.x ‘singleton‘ attribute in use - upgrade to ‘scope‘ declaration", ele);
        }
        else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
           bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
        }
        else if (containingBean != null) {
           // Take default from containing bean in case of an inner bean definition.
           bd.setScope(containingBean.getScope());
        }
 
        if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
        bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
        }
 
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
           lazyInit = this.defaults.getLazyInit();
        }
        bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
 
        String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
        bd.setAutowireMode(getAutowireMode(autowire));
 
        if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
           String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
        }
 
        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));
        }
 
        if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
        bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
        }
 
        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);
        }
 
        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);
        }
 
        if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
        bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
        }
        if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
        bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
        }
 
        return bd;
    }

 这个方法完成了对所有bean属性的解析。

2.3解析子元素meta

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

   <bean id="user" class="com.example.demo.entity.User">
        <property name="name" value="zhangsanfeng"></property>
        <property name="age" value="14"></property>
        <meta key="test" value="aaaa"/>
</bean>

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

 2.4解析子元素lookup-method

同样,子元素lookup-method似乎并不是很常用,但是在某些时候它的确非常有用的,通常我们称它为获取器注入。

我们看看具体的应用

(1)     首先创建一个父类。

public class User1 {
       public void showMe()
       {
              System.out.println("i am user");
       }
}

 (2)     创建其子类并覆盖showMe方法。

public class Teacher extends User1{
       public void showMe()
       {
              System.out.println("i am teacher");
       }
}

 (3)     创建调用方法。

public abstract class GetBeanTest {
    public void showMe()
    {
           this.getBean().showMe();
    }
    public abstract User1 getBean();
}

 (4)     创建测试方法。

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class Main {
    public static void main(String[] args)
    {
           ApplicationContext bf = new ClassPathXmlApplicationContext("lookupTest.xml");
           GetBeanTest test = (GetBeanTest)bf.getBean("getBeanTest");
           test.showMe();
    }
}

 (5)     配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
 
    <bean id="getBeanTest" class="com.example.demo.entity.GetBeanTest">
        <lookup-method name = "getBean" bean = "teacher"/>
    </bean>
    <bean id = "teacher" class = "com.example.demo.entity.Teacher"></bean>
</beans>

 例子写完了,可能你会觉得抽象方法还没有实现,怎么可能直接调用呢?答案是在配置文件中,动态的将teacher所代表的bean作为getBean的返回值。

以上是lookup-method子元素所提供的大致功能,然后我们来看它的属性提取源码。   

 public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
           Node node = nl.item(i);
           // 仅当在Spring默认bean的子元素下且为<lookup-method时有效。
           if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
               Element ele = (Element) node;
               // 获取要修饰的方法
               String methodName = ele.getAttribute(NAME_ATTRIBUTE);
               // 获取配置返回的bean
               String beanRef = ele.getAttribute(BEAN_ELEMENT);
               LookupOverride override = new LookupOverride(methodName, beanRef);
               override.setSource(extractSource(ele));
               overrides.addOverride(override);
           }
        }
    }

 上面的代码很眼熟,似乎与parseMetaElements的代码大同小异,最大的区别就是在if判断中的节点名称在这里被修改为LOOKUP_METHOD_ELEMENT。还有,在数据存储上面通过使用LookupOverride类型的实体类类进行数据承载并记录在AbstractBeanDefinition中的MethodOverrides属性中。

以上是关于Spring IOC 标签的解析的主要内容,如果未能解决你的问题,请参考以下文章

[死磕 Spring 12/43] --- IOC 之解析 bean 标签:解析自定义标签

[死磕 Spring 8/43] --- IOC 之解析 bean 标签:开启解析进程

[死磕 Spring 13/43] --- IOC 之解析自定义标签 todo

死磕 Spring----- IOC 之解析自定义标签

[死磕 Spring 9/43] --- IOC 之解析 bean 标签:BeanDefinition

spring源码解析之IOC容器——依赖注入