[Spring]RootBeanDefinition、GenericBeanDefinition、ChildBeanDefinition

BeanDefinition

从我的上一篇文章你可以知道,BeanDefinition定义了一个Bean大概需要什么信息,给这些信息定义了一些getter和setter方法,作为接口层它不负责定义具体的参数,AbstractBeanDefinition对这个接口进行了实现,它提供了一个BeanDefinition所需要具备的基本能力,如果需要扩展功能,那么可以对这个类进行继承,再添砖加瓦即可。今天我们来看看AbstractBeanDefinition的三个子类:

  • RootBeanDefinition
  • ChildBeanDefinition
  • GenericBeanDefinition

RootBeanDefinition

从Spring2.5开始,RootBeanDefinition仅作为运行时的BeanDefinition视图。如果需要编程定义BeanDefinition,那么推荐使用GenericBeanDefinition。
Spring的解释是:GenericBeanDefinition的优势在于,它允许动态定义父依赖项,而不是一个以"硬编码"定义BeanDefinition的角色。
也就是说,bean的一般形式是以GenericBeanDefinition的标准形式存在的,在特定的时机,会将GenericBeanDefinition转成RootBeanDefinition

RootBeanDefinition特点

  • 存储BeanDefinitionHolder对象,该对象用来存储Bean的name与alias的对应关系,提供BeanDefinition的访问方式
  • AnnotatedElement,这个是Java反射包的注解,开发者可以通过它来操作注解。如平时常见到的getAnnotation()getAnnotations()isAnnotationPresent()
  • allowCaching,是否允许缓存
  • isFactoryMethodUnique,工厂方法是否唯一
  • targetType,与Java泛型相关的工具类
  • resolvedTargetType,当前类持有的Class对象
  • factoryMethodToIntrospect,缓存工厂方法
  • Executable,缓存已经解析的构造函数或者方法,Executable是Method和Construct的父类
  • constructorArgumentsResolved,布尔值,表示构造函数是否被解析完毕
  • resolvedConstructorArguments,缓存被完全解析的构造函数参数
  • postProcessed,是否被MergedBeanDefinitionPostProcessor处理
  • beforeInstantiationResolved,已启动实例化前的后处理器
  • externallyManagedInitMethods,初始化时的回调函数
  • externallyManagedDestroyMethods,销毁时的回调函数
  • constructorArgumentLockpostProcessingLock,对象锁,有兴趣的可以自行深入。这里不做阐述

此外,RootBeanDefinition不可以进行setParentName()操作。
可以看到它存储了许多关于注解与反射相关的信息,这或许会在某些时机用到这些信息进行反射。

Demo

通过Java代码来实例化一个RootBeanDefinition

Root
public class Root {

    private String name;

    private String description;

    private boolean isRoot;

    public Root() {

    }

    /**
     * 构造函数
     * @param name 名称 对应的index为 0
     * @param description 描述 对应的index为 1
     * @param isRoot 是否是父BeanDefinition 对应的index为 2
     */
    public Root(String name, String description, Boolean isRoot) {
        this.name = name;
        this.description = description;
        this.isRoot = isRoot;
    }

    @Override
    public String toString() {
        return "Root{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                ", isRoot=" + isRoot +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean getIsRoot() {
        return isRoot;
    }

    public void setIsRoot(boolean root) {
        isRoot = root;
    }
}
Child
package com.xjm.model;

/**
 * @author jaymin
 * 2020/11/29 14:21
 */
public class Child {
    private String name;

    private String description;

    private boolean isRoot;

    private String parentName;

    public Child(String name, String description, boolean isRoot, String parentName) {
        this.name = name;
        this.description = description;
        this.isRoot = isRoot;
        this.parentName = parentName;
    }

    public Child() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean getIsRoot() {
        return isRoot;
    }

    public void setIsRoot(boolean root) {
        isRoot = root;
    }

    public String getParentName() {
        return parentName;
    }

    public void setParentName(String parentName) {
        this.parentName = parentName;
    }

    @Override
    public String toString() {
        return "Child{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                ", isRoot=" + isRoot +
                ", parentName='" + parentName + '\'' +
                '}';
    }
}

这里定义的对象待会会复用,我就不重新定义了

RootBeanDefinitionDemo
package com.xjm.bean.definition;

import com.xjm.model.Root;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author jaymin<br>
 * <p>
 * 2020/11/29 13:28
 */
@SuppressWarnings("all")
public class RootBeanDefinitionDemo {

    /**
     * 通过构造器注入参数获取RootBeanDefinition
     *
     * @return
     */
    public static RootBeanDefinition getRootBeanDefinitionByConstruct() {
        ConstructorArgumentValues cargs = new ConstructorArgumentValues();
        cargs.addIndexedArgumentValue(0, "rootA");
        cargs.addIndexedArgumentValue(1, "This is a RootBeanDefinition");
        cargs.addIndexedArgumentValue(2, true);
        RootBeanDefinition rootBeanDefinitionA = new RootBeanDefinition(Root.class, cargs, null);
        return rootBeanDefinitionA;
    }

    /**
     * 通过Setter的方式注入参数,返回RootBeanDefinition
     *
     * @return
     */
    public static RootBeanDefinition getRootBeanDefinitionBySetter() {
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("name", "rootB");
        propertyValues.addPropertyValue("description", "This is a RootBeanDefinition");
        propertyValues.addPropertyValue("isRoot", true);
        RootBeanDefinition rootBeanDefinitionB = new RootBeanDefinition(Root.class, null, propertyValues);
        return rootBeanDefinitionB;
    }

    public static void main(String[] args) {
        // 1. 构建一个空的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 2.创建一个RootBeanDefinition
        RootBeanDefinition rootBeanDefinitionA = getRootBeanDefinitionByConstruct();
        RootBeanDefinition rootBeanDefinitionB = getRootBeanDefinitionBySetter();
        applicationContext.registerBeanDefinition("rootA", rootBeanDefinitionA);
        applicationContext.registerBeanDefinition("rootB", rootBeanDefinitionB);
        // 这里埋一个点,直到这里才调用了Root对象的构造函数
        applicationContext.refresh();
        Root rootA = ((Root) applicationContext.getBean("rootA"));
        Root rootB = ((Root) applicationContext.getBean("rootB"));
        System.out.println(rootA.toString());
        System.out.println(rootB.toString());
    }
}
root

ChildBeanDefinition

支持setParentName(),参数为父RootBeanDefinition的name,通过构造函数的方式进行设置继承关系.Spring的注释写到多数情况下GenericBeanDefinition可以替代ChildBeanDefinition,所以这里就不详细讲了,提供一个demo供大家品味.

ChildBeanDefinitionDemo

package com.xjm.bean.definition;

import com.xjm.model.Child;
import com.xjm.model.Root;
import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author jaymin<br>
 * <p>
 * 2020/11/29 14:20
 */
@SuppressWarnings("all")
public class ChildBeanDefinitionDemo {

    public static void main(String[] args) {
        // 1. 构建一个空的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        String parentName = "rootB";
        String childName = "child";
        RootBeanDefinition rootBeanDefinition = RootBeanDefinitionDemo.getRootBeanDefinitionBySetter();
        ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition(parentName);
        childBeanDefinition.setBeanClass(Child.class);
        childBeanDefinition.getPropertyValues().addPropertyValue("parentName", parentName);
        childBeanDefinition.getPropertyValues().addPropertyValue("isRoot", false);
        // 注册
        applicationContext.registerBeanDefinition(parentName, rootBeanDefinition);
        applicationContext.registerBeanDefinition(childName, childBeanDefinition);
        // 刷新上下文
        applicationContext.refresh();
        Root root = applicationContext.getBean(Root.class);
        Child child = (Child) applicationContext.getBean(childName);
        System.out.println(root.toString());
        System.out.println(child.toString());
    }
}

GenericBeanDefinition

提供了setParentName()的方法,并且是setter的方式,通过读Spring的注释,它如此描述道:

GenericBeanDefinition是一站式商店,用于标准bean定义。像任何bean定义一样,它允许指定一个类以及可选的构造函数参数值和属性值。另外,可以通过“ parentName”属性灵活地配置从父bean定义派生的内容。


child

GenericBeanDefinitionDemo

package com.xjm.bean.definition;

import com.xjm.model.Child;
import com.xjm.model.Root;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author jaymin
 * 2020/11/29 14:57
 */
public class GenericBeanDefinitionDemo {

    public static GenericBeanDefinition getRootBeanDefinition() {
        GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
        rootBeanDefinition.setBeanClass(Root.class);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.add("name", "root")
                .add("description", "I am a rootBeanDefinition")
                .add("isRoot", true);
        rootBeanDefinition.setPropertyValues(propertyValues);
        return rootBeanDefinition;
    }

    public static GenericBeanDefinition getChildBeanDefinition() {
        GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
        childBeanDefinition.setBeanClass(Child.class);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.add("parentName", "root");
        childBeanDefinition.setParentName("root");
        childBeanDefinition.setPropertyValues(propertyValues);
        return childBeanDefinition;
    }

    public static void main(String[] args) {
        // 1. 构建一个空的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        GenericBeanDefinition rootBeanDefinition = getRootBeanDefinition();
        GenericBeanDefinition childBeanDefinition = getChildBeanDefinition();
        applicationContext.registerBeanDefinition("root", rootBeanDefinition);
        applicationContext.registerBeanDefinition("child", childBeanDefinition);
        applicationContext.refresh();
        Root root = applicationContext.getBean(Root.class);
        Child child = applicationContext.getBean(Child.class);
        System.out.println(root.toString());
        System.out.println(child.toString());
    }
}
GenericBeanDefinition

可以看到,我们只用了一个GenericBeanDefinition就可以完成RootBeanDefinition和RootBeanDefinition能做到的事情。只需要setParentName(),就可以拷贝另一个BeanDefinition的信息。易于扩展

留给读者的话

Spring源码工程非常地庞大,但是提供了大量的注释,有的时候光变量就一大堆,我们怎么让这些东西给我们留下一个深刻的印象呢,唯有实践加上思考。我个人知识储量尚未足够丰富,只能留下一些代码,有兴趣的朋友可以自行跟着实现一下,感受一下Spring的强大之处。至于更深层的原理,目前尚未能完全讲的透彻。如有不对的地方,欢迎指正。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容