逐行阅读Spring5.X源码(二) BeanDefinition的父接口-AttributeAccessor 、BeanMetadataElement ,进阶

温馨提示:如果读者刚接触spring源码,建议从本专题第一篇读起,这样知识点才能串联起来。
本片博客你将学到BeanDefinition的父接口:
1. AttributeAccessor
        1.1 实现类 AttributeAccessorSupport
2. BeanMetadataElement
       2.1 实现类 BeanMetadataAttributeAccessor
       2.2 属性封装对象 BeanMetadataAttribute

       上回书讲了BeanDefinition的基础知识,本篇文章咱们详细讨论BeanDefinition家族体系。 在讲解之前笔者再一次祭出BeanDefinition家族继承关系图:
image.png

1、BeanDefinition的父接口

       上一篇博文详细讲了接口BeanDefinition的功能,笔者打算围绕该接口进行扩展。
       首先看下BeanDefinition的父接口有哪些:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
  .....省略
}

        BeanDefinition 继承了AttributeAccessorBeanMetadataElement 两个接口。也就是说BeanDefinition的实现类同时实现了BeanDefinition 、AttributeAccessor、BeanMetadataElement三个接口的功能。

1.1 AttributeAccessor

/**
 * Interface defining a generic contract for attaching and accessing metadata
 * to/from arbitrary objects.
 *定义用于附加和访问BeanDefinition元数据的通用的接口,来自任意对象
 */
public interface AttributeAccessor {
    /**
     * Set the attribute defined by {@code name} to the supplied    {@code value}.
    将name的属性值设置为value*/
    void setAttribute(String name, Object value);
    /**
     * Get the value of the attribute identified by {@code name}.
     * Return {@code null} if the attribute doesn't exist.
   获取name的属性值,如果该属性不存在,则返回Null*/
    Object getAttribute(String name);
    /**
     * Remove the attribute identified by {@code name} and return its value.
     * Return {@code null} if no attribute under {@code name} is found.
     删除name的属性值,如果找不到Nmae属性的值则返回Null*/
    Object removeAttribute(String name);
    /**
     * Return {@code true} if the attribute identified by {@code name} exists.
    如果name属性值存在则返回true,否者返回false*/
    boolean hasAttribute(String name);
    /**
     * Return the names of all attributes.
    返回所有的属性名称
     */
    String[] attributeNames();

}

       该接口定义用于附加和访问BeanDefinition元数据,我们知道,接口只是定义了操作方法,这个接口的方法很简单,但我们看不出这个接口到底代表啥意思呢,回头看继承图,找到这个接口唯一的实现类AttributeAccessorSupport:

1.1.1AttributeAccessorSupport


public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
    /** Map with String keys and Object values. */
    //用于存放属性键值对
    private final Map<String, Object> attributes = new LinkedHashMap<>();
    //设置属性值
    @Override
    public void setAttribute(String name, @Nullable Object value) {
        Assert.notNull(name, "Name must not be null");
        if (value != null) {
            this.attributes.put(name, value);
        }
        else {
            removeAttribute(name);
        }
    }
    //获取属性值
    @Override
    @Nullable
    public Object getAttribute(String name) {
        Assert.notNull(name, "Name must not be null");
        return this.attributes.get(name);
    }
    //删除属性值
    @Override
    @Nullable
    public Object removeAttribute(String name) {
        Assert.notNull(name, "Name must not be null");
        return this.attributes.remove(name);
    }
   //判断是否有属性值
    @Override
    public boolean hasAttribute(String name) {
        Assert.notNull(name, "Name must not be null");
        return this.attributes.containsKey(name);
    }
    //获取所有属性值名字
    @Override
    public String[] attributeNames() {
        return StringUtils.toStringArray(this.attributes.keySet());
    }

    //内部使用,属性值的拷贝
    /**
     * Copy the attributes from the supplied AttributeAccessor to this accessor.
     * @param source the AttributeAccessor to copy from
     */
    protected void copyAttributesFrom(AttributeAccessor source) {
        Assert.notNull(source, "Source must not be null");
        String[] attributeNames = source.attributeNames();
        for (String attributeName : attributeNames) {
            setAttribute(attributeName, source.getAttribute(attributeName));
        }
    }
    //重写equals方法,判断是否与别的属性类共用一个存储结构,即判断LinkedHashMap是否相等
    @Override
    public boolean equals(Object other) {
        return (this == other || (other instanceof AttributeAccessorSupport &&
                this.attributes.equals(((AttributeAccessorSupport) other).attributes)));
    }
    @Override
    public int hashCode() {
        return this.attributes.hashCode();
    }

}

       其实,这个实现类我们还是看不出他跟BeanDefinition有什么关系,但是我们搞清楚一点,就是AttributeAccessorSupport 就是维护一个LinkedHashMap而已,BeanDefinition的某些属性值就存储在这个LinkedHashMap中,是哪些属性值呢?看下面代码:

public class SpringTest {
    public static void main(String[] args) throws InterruptedException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册配置类
        context.register(Config.class);
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClassName("com.InterService");
        //设置属性值
        beanDefinition.setAttribute("AttributeAccessor","源码之路");
        //将beanDefinition注册到spring容器中
        context.registerBeanDefinition("interService",beanDefinition);
        //加载或者刷新当前的配置信息
        context.refresh();
        //拿到属性信息
        String[] attributes = context.getBeanDefinition("interService").attributeNames();
    }
}
image.png

       我们知道BeanDefinition用来描述你的业务类,那么,谁来描述BeanDefitino呢?就是通过设置属性值来描述的!比如beanDefinition的代理模式等,以后会讲到。

1.2 BeanMetadataElement

public interface BeanMetadataElement {
    //获取源对象,可能返回null
    Object getSource();
 }

啥意思?懵圈!
       BeanDefinition中存了业务类在虚拟机中的class,这个上篇博文讲了。但是这个class文件存在你电脑硬盘哪里呢?或者这么讲,getSource翻译成中文就是获取源,对象的源是class,那class的源就是你硬盘上的文件。看下面代码:

public class SpringTest {
    public static void main(String[] args) throws InterruptedException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册配置类
        context.register(Config.class);
        //加载或者刷新当前的配置信息
        context.refresh();
        //获取InterService.class的源
        System.out.println(context.getBeanDefinition("interService").getSource());
    }
}

打印结果:


image.png

1.2.1 BeanMetadataAttributeAccessor

       BeanMetadataElement 只是提供获取源的方法,真正的实现是在它的实现类BeanMetadataAttributeAccessor中,我们看一下它的源码:

public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {

    @Nullable
    private Object source;


    /**
     * Set the configuration source {@code Object} for this metadata element.
     * <p>The exact type of the object will depend on the configuration mechanism used.
     * 设置源
     */
    public void setSource(@Nullable Object source) {
        this.source = source;
    }

    @Override
    @Nullable
    //获取源
    public Object getSource() {
        return this.source;
    }


    /**
     * Add the given BeanMetadataAttribute to this accessor's set of attributes.
     * @param attribute the BeanMetadataAttribute object to register
     */
    //设置属性值,如果已经存在就覆盖,不存在就添加,BeanMetadataAttribute封装了键值对
    public void addMetadataAttribute(BeanMetadataAttribute attribute) {
        super.setAttribute(attribute.getName(), attribute);
    }
    /**
     * Look up the given BeanMetadataAttribute in this accessor's set of attributes.
     * @param name the name of the attribute
     * @return the corresponding BeanMetadataAttribute object,
     * or {@code null} if no such attribute defined
     * 根据名字获取属性键值对的封装对象BeanMetadataAttribute
     */
    @Nullable
    public BeanMetadataAttribute getMetadataAttribute(String name) {
        return (BeanMetadataAttribute) super.getAttribute(name);
    }
    //设置属性值,name表示键,value表示值
    @Override
    public void setAttribute(String name, @Nullable Object value) {
        super.setAttribute(name, new BeanMetadataAttribute(name, value));
    }
    @Override
    @Nullable
    //根据键获取属性值
    public Object getAttribute(String name) {
        BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
        return (attribute != null ? attribute.getValue() : null);
    }
    @Override
    @Nullable
    //移除属性值,并返回值,不存在就返回空
    public Object removeAttribute(String name) {
        BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.removeAttribute(name);
        return (attribute != null ? attribute.getValue() : null);
    }
}

BeanMetadataAttributeAccessor不但继承了BeanMetadataElement还实现了AttributeAccessorSupport,换言之,既可以操作属性值,也可以操作源。

1.2.2 BeanMetadataAttribute

上文代码中的类BeanMetadataAttribute其实就是对属性的键值对进行了封装,如下:

public class BeanMetadataAttribute implements BeanMetadataElement {
    /**
     * 属性信息
     */
    private final String name;      // 属性名
    private final Object value;     // 属性值
    private Object source;      // 属性所属对象,,这里的源是属性值对象的源,不是BeanDefinition中所封装的业务类的源
    /**
     * 构造器(设置属性信息)
     */
    public BeanMetadataAttribute(String name, Object value) {
        Assert.notNull(name, "Name must not be null");
        this.name = name;
        this.value = value;
    }

    /**
     * 获取属性名、属性值以及获取、设置属性所属对象
     */
    public String getName() {
        return this.name;
    }
    public Object getValue() {
        return this.value;
    }
    public void setSource(Object source) {
        this.source = source;
    }
    @Override
    public Object getSource() {
        return this.source;
    }
    /**
     * 判断属性是否相等
     */
    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof BeanMetadataAttribute)) {
            return false;
        }
        BeanMetadataAttribute otherMa = (BeanMetadataAttribute) other;
        return (this.name.equals(otherMa.name) &&
                ObjectUtils.nullSafeEquals(this.value, otherMa.value) &&
                ObjectUtils.nullSafeEquals(this.source, otherMa.source));
    }
    @Override
    public int hashCode() {
        return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
    }
    @Override
    public String toString() {
        return "metadata attribute '" + this.name + "'";
    }
}

小总结:

image.png

       至此,我们讲完了BeanDefinition接口、父接口、父接口实现类的源代码,BeanDefinition的实现类会继承BeanDefinition父接口的实现类。到此为止,BeanDefinition的实现类可以操作属性和源,下面讲解BeanDefinition的实现类如何实现BeanDefinition接口。

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