3-AbstractBeanDefinition

背景简介

出现的原因

之前我们分析了 BeanDefinition 的职责及在接口中做的基本操作。为了满足不同的具体用途,我们会定义不同的BeanDefinition的具体实现。

但是这些实现除了一些具体的属性和操作是定制的,大部分都是模版性质的代码或者说都是共同的功能,所以抽出一个基本的实现类来实现通用操作就变得有必要了。

这种模式我们在之前看集合类的那一系列操作经常看到,这些设计参考的设计模式是适配器模式/模版方法模式

区分这两种模式的一个比较明显的点是:

  • 你在Abstract***中将主干逻辑实现了,专门设置/留下一些核心方法让子类定制,这个一般是模版方法模式
  • 如果你在Abstract***中只是将通用的一些逻辑从头搞定,或者只是做了默认实现,这个一般是适配器模式

职责

AbstractBeanDefinitionBeanDefinition中规定的方法中最通用的一些操作进行了实现,方便子类将注意力专门放到自己关注的领域。

注意点

源码

类继承结构分析

思路引导

我们在读 Spring 源码时经常会遇到一个类要实现的接口是 n 多个接口继承出来的。继承树超级复杂,我们如果要对这样的接口进行一个 Abstract****的实现,涉及的东西超级多。

但是我们这样想一下:

接口进行了 n 多个的继承,接口继承的目的很明显:功能加强/融合。一般很少有接口仅仅是用来定义的,接口一般都对应自己定义的功能的默认实现。

我们直接继承那个默认实现,是不是就可以不用专门在我们的Abstract****中全都写一遍了?

这种思路有个致命的缺点:

Java 的单继承带来了很多的便利,例如从根源上避免了 C++ 的多继承引起的问题,也不会了解虚基类的那些。

但是还是会带来一些问题的,例如如果你要实现的那个接口如果它的继承数量 n >= 2 ,可能找不到合适的功能默认实现类来继承。

如果遇到这个问题,我门可以考虑慢慢继承。。。。。二合一、二合一的来。。。。有点像最开始的XmlBeanFactory的继承树那样。

实践

我们来分析一下——BeanDefinition继承了两个接口:

  1. AttributeAccessor
  2. BeanMetadataElement

这明显 >=2 了,我们试一下二合一、二合一的来。。。。

整合AttributeAccessor

先找其中一个的实现类,我们找 AttributeAccessor吧。这个接口是一个属性的增删改查的功能。我们找到了它的一个实现类AttributeAccessorSupport,用LinkedHashMap为内部数据结构做了一个基本实现。

整合BeanMetadataElement

这个接口定义了一个获得 bean 的源数据内容的函数。我们直接继承AttributeAccessorSupport ,然后实现BeanMetadataElement。这样就把这两个功能整到一起了。有了BeanMetadataAttributeAccessor这个类,就是内部定义了一个source的属性用来存源数据内容,剩下的AttributeAccessor的功能都依靠父类的实现。

整合进来

上面我们用BeanMetadataAttributeAccessor完成了两个功能的整合。接下来直接继承它就行了。然后把所有的注意力都放到BeanDefinition中定义的方法中来。

结果对比

1.png

唯一比较突兀的是考虑到一些复制操作,AbstractBeanDefinition实现了Clonable接口。虽然继承树看着贼鸡儿乱,但是根据上面的分析,基本就透彻了。此处不再赘述,我们直接看AbstractBeanDefinition中对BeanDefinition中定义的方法的描述。

其实这也是一个类注重一个功能,是设计模式中的单一职责原则

内部源码解析

AbstractBeanDefinition主要做了三件事:

  1. 扩展了一下 BD 中定义的常量。
  2. 针对 BD中定义的那一串 getter/setter方法,定义了一把子实例属性。
  3. 多定义了一些属性,并定义了一些工具方法。

常量扩展

// 对 Bean
/**
 * 默认生命周期,如果有父容器,就跟随父容器的生命周期;如果没有父容器,就是单例声明周期。
 */
public static final String SCOPE_DEFAULT = "";

/**
 * 不自动注入属性
 *
 */
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;

/**
 * 通过属性命名自动注入属性
 */
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

/**
 * 通过属性类型自动注入类型
 */
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;

/**
 * 此 Bean 的实例在构建时采用构造函数注入。
 * 【当然,这里只是说使用构造函数,并通过构造函数带进去一些东西。构造出实例后还是可以通过 上面的配置来进行
 *  根据 命名/类型 继续进行属性注入】
 */
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;

/**
 * 构建此 Bean 的实例时不进行任何依赖检测
 */
public static final int DEPENDENCY_CHECK_NONE = 0;

/**
 * 构建此 Bean 的实例时只检测对象依赖【复杂依赖】
 */
public static final int DEPENDENCY_CHECK_OBJECTS = 1;

/**
 * 构建此 Bean 的实例时只检测常用类型依赖【简单依赖】
 */
public static final int DEPENDENCY_CHECK_SIMPLE = 2;

/**
 * 构建此 Bean 的实例时检测所有依赖
 */
public static final int DEPENDENCY_CHECK_ALL = 3;

/**
 * 一个常量,在记录 Bean 的销毁要调用的方法时如果设置成这个值就表明这不是个具体的方法名。方法名称需要
 * 自行推断。
 *
 * 【这个常量的值设置成这样是为了保证不会有某些很变态的方法命名和这个常量的值冲突了】
 */
public static final String INFER_METHOD = "(inferred)";

实例变量定义

看名字基本就能和 BeanDefinition中的getter/setter对应上了。不再赘述。

@Nullable
private volatile Object beanClass;
@Nullable
private String scope = SCOPE_DEFAULT;
private boolean abstractFlag = false;
private boolean lazyInit = false;
@Nullable
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
@Nullable
private String factoryBeanName;
@Nullable
private String factoryMethodName;
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
@Nullable
private MutablePropertyValues propertyValues;
@Nullable
private String initMethodName;
private int role = BeanDefinition.ROLE_APPLICATION;
@Nullable
private String description;
@Nullable
private Resource resource;

扩展的变量及方法

扩展的变量

/**
 * 存储此 BD 的 Qualifier 值。这个有点像是别名的,可以在声明 Bean 时写上 n 个,在要用时,通过
 * 使用 Qualifier 进行限制。
 **/
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();

/**
 * 指明对此 Bean 中的依赖属性如何寻找到对应的值
 **/
private int autowireMode = AUTOWIRE_NO;

/**
 * 配置是否对此 Bean 中配置的依赖进行检测
 **/ 
private int dependencyCheck = DEPENDENCY_CHECK_NONE;

/**
 * 用来记录生成此 Bean 实例的一个工厂方法引用【算是最直白最爽的一种实例化方法了】
 **/
@Nullable
private Supplier<?> instanceSupplier;

/**
 * 设置是否允许通过特殊手段接触 Bean 的 Class 中的非公有方法【个人感觉主要是在配置实例化 Bean 时找构造
 * 函数时的一些策略用吧】
 **/
private boolean nonPublicAccessAllowed = true;

/**
 * 在配置实例化 Bean 时找构造函数时是否采用宽松策略策略用吧
 **/
private boolean lenientConstructorResolution = true;

/**
 * 用来保存在 BeanDefinition 配置中的一些方法覆盖的设置
 **/
@Nullable
private MethodOverrides methodOverrides;

/**
 * 配置当要销毁实例 Bean 时要调用的方法
 **/
@Nullable
private String destroyMethodName;

/**
 * 指明配置的初始化方法是不是默认的那个【????】【可能是为了简化逻辑提供的一个标记量?】
 **/
private boolean enforceInitMethod = true;

/**
 * 指明配置的销毁方法是不是默认的那个【????】【可能是为了简化逻辑提供的一个标记量?】
 **/
private boolean enforceDestroyMethod = true;

/**
 * 指明此 BD 是否是合成的
 *【合成的 = 用户自己定义的 = 不是应用程序定义的 = 不用执行一些后处理器钩子???】【最后一点不确定】
 **/
private boolean synthetic = false;

可见,这些新增的属性主要是以下几个方面:

  1. 对 Bean 的一些功能的增加/增强
  2. 一些捷径标记的设定

扩展的方法

大概有两种,一种是提供更细粒度的控制,直接粘贴代码,不再赘述。

/**
 * Return a description of the resource that this bean definition
 * came from (for the purpose of showing context in case of errors).
 */
@Override
@Nullable
public String getResourceDescription() {
    return (this.resource != null ? this.resource.getDescription() : null);
}

/**
 * Set a description of the resource that this bean definition
 * came from (for the purpose of showing context in case of errors).
 */
public void setResourceDescription(@Nullable String resourceDescription) {
    this.resource = (resourceDescription != null ? new DescriptiveResource(resourceDescription) : null);
}

/**
 * Return the originating BeanDefinition, or {@code null} if none.
 * Allows for retrieving the decorated bean definition, if any.
 * <p>Note that this method returns the immediate originator. Iterate through the
 * originator chain to find the original BeanDefinition as defined by the user.
 */
@Override
@Nullable
public BeanDefinition getOriginatingBeanDefinition() {
    return (this.resource instanceof BeanDefinitionResource ?
            ((BeanDefinitionResource) this.resource).getBeanDefinition() : null);
}

/**
 * Set the originating (e.g. decorated) BeanDefinition, if any.
 */
public void setOriginatingBeanDefinition(BeanDefinition originatingBd) {
    this.resource = new BeanDefinitionResource(originatingBd);
}

第二种是为 Bean 的初始化提供了一些辅助接口。

/**
 * 校验此 BD 的定义有没有明显的问题
 *
 * @throws BeanDefinitionValidationException in case of validation failure
 */
public void validate() throws BeanDefinitionValidationException {
  // 如果使用工厂方法,那就不能做方法增强
    if (hasMethodOverrides() && getFactoryMethodName() != null) {
        throw new BeanDefinitionValidationException(
                "Cannot combine static factory method with method overrides: " +
                        "the static factory method must create the instance");
    }

    if (hasBeanClass()) {
        prepareMethodOverrides();
    }
}

/**
 * 对要增强的方法过一遍。
 * 
 * 如果有明显的配置不合理,例如方法不存在,直接报错结束。
 * 如果有捷径可走,比如方法没有重载,标记一下方便后续处理
 *
 * @throws BeanDefinitionValidationException in case of validation failure
 */
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    // Check that lookup methods exists.
    if (hasMethodOverrides()) {
        Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
        synchronized (overrides) {
            for (MethodOverride mo : overrides) {
                prepareMethodOverride(mo);
            }
        }
    }
}

/**
 * 具体实现方法,调用 core 包的 util 找到对应的数量即可。那个 util 应该是用反射处理了一下。
 *
 * @param mo the MethodOverride object to validate
 * @throws BeanDefinitionValidationException in case of validation failure
 */
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
    int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
    if (count == 0) {
        throw new BeanDefinitionValidationException(
                "Invalid method override: no method with name '" + mo.getMethodName() +
                        "' on class [" + getBeanClassName() + "]");
    } else if (count == 1) {
        // 只有一个,说明我们要覆盖的方法不需要重写
        // Mark override as not overloaded, to avoid the overhead of arg type checking.
        mo.setOverloaded(false);
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容

  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 1,206评论 0 5
  • 1.import static是Java 5增加的功能,就是将Import类中的静态方法,可以作为本类的静态方法来...
    XLsn0w阅读 1,228评论 0 2
  • 面向对象主要针对面向过程。 面向过程的基本单元是函数。 什么是对象:EVERYTHING IS OBJECT(万物...
    sinpi阅读 1,057评论 0 4
  • java继承 继承的概念 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。 继承就是子类继...
    863cda997e42阅读 670评论 0 1
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,655评论 0 11