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中存在三种实现:RootBeanDefinition、ChildBeanDefinition、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属性中。

原文地址:https://www.cnblogs.com/wcj-java/p/9221235.html

时间: 2024-10-02 09:55:31

Spring IOC 标签的解析的相关文章

Spring?IOC设计原理解析:本文乃学习整理参考而来

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入

Spring IoC源码解析之getBean

一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到实例化所有的非懒加载的单实例Bean的finishBeanFactoryInitialization(beanFactory)的方法: protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory bea

Spring IOC设计原理解析(1)

一. 什么是Ioc/DI?大家都知道IOC 是控制反转(Inversion of Control,缩写为IoC)是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度. 之前我们的bean对象的创建是交由调用者通过new 的方式进行创建的, 那么这样的方式有一些不足之处: bean的整个生命周期都交给了这个调用者了, 过程如下:并将该对象的引用返回.如果该返回被一个引用接收,由于对象的引用不为0,对象依然存在,不会被垃圾回收. 而另外一个对象调用这个class 的时候还是会创建一个对

Spring IOC初始化深度解析

1.前言 本文是基于JAVA配置方法对Spring IOC进行分析,掌握Spring IOC初始化流程对于我们更好的使用Spring.学习Spring还是很有帮助的,本文所使用的的Spring版本为5.2.2,下面进入分析 2.初始化流程概要图 PS:图比较大,可以选择新标签页打开 3.初始化流程详解 3.1核心对象说明 beanDefinitionMap:单例bean缓存池(对于使用FactoryBean创建出来的bean,并未保存在此map中!FactoryBean的情况,这里面存的是bea

Spring IoC源码解析——Bean的创建和初始化

Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,MyBatis框架等组合使用. IoC介绍 IoC是什么 Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控

spring ioc 源码解析

什么是ioc? 通俗的解释是:(spring)框架中,完成对象的创建和注入的容器. springIOC体系结构: spring IOC的创建是典型的工厂模式,这一系列的bean工厂如上所示. 其核心是BeanFactory,它是IOC容器最顶层的接口类,定义了IOC容器的基本功能规范, 其中重点可关注:BeanFactory,ListableBeanFactory,HierarchicalBeanFactory,AutowireCapableBeanFactory,DefaultListable

Spring IOC源码解析

Spring的核心是IoC容器和AOP,它们是Spring架构体系中的核心基础.平常使用通过简单配置即可,没有深究其底层实现,因而常常不知所以然,殊不知Spring已经帮助我们完成了太多事情.因此有必要对其实现进行学习,加深对其的理解和应用. IoC(Inverse of Control):控制反转,将对象的创建权利交由Spring来管理. 继承体系 IoC容器的实现又依赖于BeanFactory和ApplicationContext,ApplicationContext继承BeanFactor

Spring IoC Bean 实例化(二)源码分析

目录 Spring IoC 依赖注入(四)构造器或工厂注入 1. doCreateBean 2. createBeanInstance 2. 实例化策略 - InstantiationStrategy 3. instantiateBean 4. autowireConstructor 4.1 整体说明 4.2 缓存匹配 4.3 无参构造器匹配 4.4 有参构造器匹配 5. ConstructorResolver 参数解析 5.1 resolveConstructorArguments 5.2 c

Spring:源码解读Spring IOC原理

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入