前面几篇文章已经学习了官网中的1.2,1.3,1.4三小结,主要是容器,Bean的实例化及Bean之间的依赖关系等。这篇文章,我们继续官网的学习,主要是BeanDefinition的相关知识,这是Spring中非常基础的一块内容,也是我们阅读源码的基石。本文主要涉及到官网中的1.31.5中的一些补充知识。同时为我们1.7小节中BeanDefinition的合并做一些铺垫

BeanDefinition是什么?

我们先看官网上是怎么解释的:

从上文中,我们可以得出以下几点结论:

  1. BeanDefinition包含了我们对bean做的配置,比如XML<bean/>标签的形式进行的配置
  2. 换而言之,Spring将我们对bean的定义信息进行了抽象,抽象后的实体就是BeanDefinition,并且Spring会以此作为标准来对Bean进行创建
  3. BeanDefinition包含以下元数据:
    • 一个全限定类名,通常来说,就是对应的bean的全限定类名。
    • bean的行为配置元素,这些元素展示了这个bean在容器中是如何工作的包括scope(域,我们文末有简单介绍),lifecycle callbacks(生命周期回调,下篇文章介绍)等等
    • 这个bean的依赖信息
    • 一些其他配置信息,比如我们配置了一个连接池对象,那么我们还会配置它的池子大小,最大连接数等等

在这里,我们来比较下,正常的创建一个bean,跟Spring通过抽象出一个BeanDefinition来创建bean有什么区别:

正常的创建一个java bean:

Spring通过BeanDefinition来创建bean:

通过上面的比较,我们可以发现,相比于正常的对象的创建过程,Spring对其管理的bean没有直接采用new的方式,而是先通过解析配置数据以及根据对象本身的一些定义而获取其对应的beandefinition,并将这个beandefinition作为之后创建这个bean的依据。同时Spring在这个过程中提供了一些扩展点,例如我们在图中所提到了BeanfactoryProcessor。这些大家先作为了解,之后在源码阶段我们再分析。

BeanDefinition的方法分析

这里对于每个字段我只保留了一个方法,只要知道了字段的含义,方法的含义我们自然就知道了

// 获取父BeanDefinition,主要用于合并,下节中会详细分析
String getParentName(); // 对于的bean的ClassName
void setBeanClassName(@Nullable String beanClassName); // Bean的作用域,不考虑web容器,主要两种,单例/原型,见官网中1.5内容
void setScope(@Nullable String scope); // 是否进行懒加载
void setLazyInit(boolean lazyInit); // 是否需要等待指定的bean创建完之后再创建
void setDependsOn(@Nullable String... dependsOn); // 是否作为自动注入的候选对象
void setAutowireCandidate(boolean autowireCandidate); // 是否作为主选的bean
void setPrimary(boolean primary); // 创建这个bean的类的名称
void setFactoryBeanName(@Nullable String factoryBeanName); // 创建这个bean的方法的名称
void setFactoryMethodName(@Nullable String factoryMethodName); // 构造函数的参数
ConstructorArgumentValues getConstructorArgumentValues(); // setter方法的参数
MutablePropertyValues getPropertyValues(); // 生命周期回调方法,在bean完成属性注入后调用
void setInitMethodName(@Nullable String initMethodName); // 生命周期回调方法,在bean被销毁时调用
void setDestroyMethodName(@Nullable String destroyMethodName); // Spring可以对bd设置不同的角色,了解即可,不重要
// 用户定义 int ROLE_APPLICATION = 0;
// 某些复杂的配置 int ROLE_SUPPORT = 1;
// 完全内部使用 int ROLE_INFRASTRUCTURE = 2;
void setRole(int role); // bean的描述,没有什么实际含义
void setDescription(@Nullable String description); // 根据scope判断是否是单例
boolean isSingleton(); // 根据scope判断是否是原型
boolean isPrototype(); // 跟合并beanDefinition相关,如果是abstract,说明会被作为一个父beanDefinition,不用提供class属性
boolean isAbstract(); // bean的源描述,没有什么实际含义
String getResourceDescription(); // cglib代理前的BeanDefinition
BeanDefinition getOriginatingBeanDefinition();

BeanDefinition的继承关系

类图如下:

1.BeanDefinition继承的接口

  • org.springframework.core.AttributeAccessor

先来看接口上标注的这段java doc

Interface defining a generic contract for attaching and accessing metadata to/from arbitrary objects.

翻译下来就是:

这个接口为从其它任意类中获取或设置元数据提供了一个通用的规范。

其实这就是访问者模式的一种体现,采用这方方法,我们可以将数据接口操作方法进行分离。

我们再来看这个接口中定义的方法:

void setAttribute(String name, @Nullable Object value);

Object getAttribute(String name);

Object removeAttribute(String name);

boolean hasAttribute(String name);

String[] attributeNames();

就是提供了一个获取属性跟设置属性的方法

那么现在问题来了,在我们整个BeanDefiniton体系中,这个被操作的数据结构在哪呢?不要急,在后文中的AbstractBeanDefinition会介绍。

  • org.springframework.beans.BeanMetadataElement

我们还是先看java doc:

Interface to be implemented by bean metadata elements that carry a configuration source object.

翻译:这个接口提供了一个方法去获取配置源对象,其实就是我们的原文件。

这个接口只提供了一个方法:

@Nullable
Object getSource();

我们可以理解为,当我们通过注解的方式定义了一个IndexService时,那么此时的IndexService对应的BeanDefinition通过getSource方法返回的就是IndexService.class这个文件对应的一个File对象。

如果我们通过@Bean方式定义了一个IndexService的话,那么此时的source是被@Bean注解所标注的一个Mehthod对象。

2.AbstractBeanDefinition

AbstractBeanDefinition的继承关系

先看一下类图:

  • org.springframework.core.AttributeAccessorSupport

可以看到这个类实现了AttributeAccerror接口,我们在上文中已经提到过,AttributeAccerror采用了访问者的涉及模式,将数据结构操作方法进行了分离,数据结构在哪呢?就在AttributeAccessorSupport这个类中,我们看下它的代码:

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);
}
}
......省略下面的代码

可以看到,在这个类中,维护了一个map,这就是BeanDefinition体系中,通过访问者模式所有操作的数据对象。

  • org.springframework.beans.BeanMetadataAttributeAccessor

这个类主要就是对我们上面的map中的数据操作做了更深一层的封装,我们就看其中的两个方法:

public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
public BeanMetadataAttribute getMetadataAttribute(String name) {
return (BeanMetadataAttribute) super.getAttribute(name);
}

可以发现,它只是将属性统一封装成了一个BeanMetadataAttribute,然后就调用了父类的方法,将其放入到map中。

我们的AbstractBeanDefinition通过继承了BeanMetadataAttributeAccessor这个类,可以对BeanDefinition中的属性进行操作。这里说的属性仅仅指的是BeanDefinition中的一个map,而不是它的其它字段。

为什么需要AbstractBeanDefinition?

对比BeanDefinition的源码我们可以发现,AbstractBeanDefinitionBeanDefinition的大部分方法做了实现(没有实现parentName相关方法)。同时定义了一系列的常量及默认字段。这是因为BeanDefinition接口过于顶层,如果我们依赖BeanDefinition这个接口直接去创建其实现类的话过于麻烦,所以通过AbstractBeanDefinition做了一个下沉,并给很多属性赋了默认值,例如:

// 默认情况不是懒加载的
private boolean lazyInit = false;
// 默认情况不采用自动注入
private int autowireMode = AUTOWIRE_NO;
// 默认情况作为自动注入的候选bean
private boolean autowireCandidate = true;
// 默认情况不作为优先使用的bean
private boolean primary = false;
........

这样可以方便我们创建其子类,如我们接下来要讲的:ChildBeanDefinition,RootBeanDefinition等等

3.AbstractBeanDefinition的三个子类

GenericBeanDefinition

  • 替代了原来的ChildBeanDefinition,比起ChildBeanDefinition更为灵活,ChildBeanDefinition在实例化的时候必须要指定一个parentName,而GenericBeanDefinition不需要。我们通过注解配置的bean以及我们的配置类(除@Bena外)的BeanDefiniton类型都是GenericBeanDefinition

ChildBeanDefinition

  • 现在已经被GenericBeanDefinition所替代了。我在5.1.x版本没有找到使用这个类的代码。

RootBeanDefinition

  • Spring在启动时会实例化几个初始化的BeanDefinition,这几个BeanDefinition的类型都为RootBeanDefinition
  • Spring在合并BeanDefinition返回的都是RootBeanDefinition
  • 我们通过@Bean注解配置的bean,解析出来的BeanDefinition都是RootBeanDefinition(实际上是其子类ConfigurationClassBeanDefinition

4.AnnotatedBeanDefinition

这个接口继承了我们的BeanDefinition接口,我们查看其源码可以发现:

AnnotationMetadata getMetadata();

@Nullable
MethodMetadata getFactoryMethodMetadata();

这个接口相比于BeanDefinition, 仅仅多提供了两个方法

  • getMetadata(),主要用于获取注解元素据。从接口的命名上我们也能看出,这类主要用于保存通过注解方式定义的bean所对应的BeanDefinition。所以它多提供了一个关于获取注解信息的方法

    • getFactoryMethodMetadata(),这个方法跟我们的@Bean注解相关。当我们在一个配置类中使用了@Bean注解时,被@Bean注解标记的方法,就被解析成了FactoryMethodMetadata

5.AnnotatedBeanDefinition的三个实现类

AnnotatedGenericBeanDefinition

  • 通过形如下面的API注册的bean都是AnnotatedGenericBeanDefinition
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Config.class);
}

这里的config对象,最后在Spring容器中就是一个AnnotatedGenericBeanDefinition

  • 通过@Import注解导入的类,最后都是解析为AnnotatedGenericBeanDefinition

ScannedGenericBeanDefinition

  • 都过注解扫描的类,如@Service,@Compent等方式配置的Bean都是ScannedGenericBeanDefinition

ConfigurationClassBeanDefinition

  • 通过@Bean的方式配置的Bean为ConfigurationClassBeanDefinition

最后,我们还剩一个ClassDerivedBeanDefinition,这个类是跟kotlin相关的类,一般用不到,笔者也不熟,这里就不管了!

总结

至此,我们算完成了BeanDefinition部分的学习,在下一节中,我将继续跟大家一起学习BeanDefinition合并的相关知识。这篇文章中,主要学习了

  1. 什么是BeanDefinition,总结起来就是一句话,Spring创建bean时的建模对象。
  2. BeanDefinition的具体使用的子类,以及Spring在哪些地方使用到了它们。这部分内容在后面的学习中很重要,画图总结如下:

1.5小结内容的补充

单例

一个单例的bean意味着,这个bean只会容器创建一次。在创建后,容器中的每个地方使用的都是同一个bean对象。这里用Spring官网上的一个原图:



在上面图片的例子中,accountDao在被其它三个bean引用,这三个引用指向的都是同一个bean。

在默认情况下,Spring中bean的默认域就是单例的。分XML跟注解两种配置方式:

<!--即使配置singleton也是单例的,这是Spring的默认配置-->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
@Component
// 这里配置singleton,默认就是singleton
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class LuBanService{ }

原型

一个原型的bean意味着,每次我们使用时都会重新创建这个bean。

在上面图片的例子中,accountDao在被其它三个bean引用,这三个引用指向的都是一个新建的bean。

两种配置方式:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
@Component
// 这里配置prototype
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LuBanService{ }

Spring官网阅读(四)BeanDefinition(上)的更多相关文章

  1. Spring官网阅读 | 总结篇

    接近用了4个多月的时间,完成了整个<Spring官网阅读>系列的文章,本文主要对本系列所有的文章做一个总结,同时也将所有的目录汇总成一篇文章方便各位读者来阅读. 下面这张图是我整个的写作大 ...

  2. Spring官网阅读(十七)Spring中的数据校验

    文章目录 Java中的数据校验 Bean Validation(JSR 380) 使用示例 Spring对Bean Validation的支持 Spring中的Validator 接口定义 UML类图 ...

  3. Spring官网阅读(十六)Spring中的数据绑定

    文章目录 DataBinder UML类图 使用示例 源码分析 bind方法 doBind方法 applyPropertyValues方法 获取一个属性访问器 通过属性访问器直接set属性值 1.se ...

  4. Spring官网阅读(十八)Spring中的AOP

    文章目录 什么是AOP AOP中的核心概念 切面 连接点 通知 切点 引入 目标对象 代理对象 织入 Spring中如何使用AOP 1.开启AOP 2.申明切面 3.申明切点 切点表达式 excecu ...

  5. Spring官网阅读(三)自动注入

    上篇文章我们已经学习了1.4小结中关于依赖注入跟方法注入的内容.这篇文章我们继续学习这结中的其他内容,顺便解决下我们上篇文章留下来的一个问题-----注入模型. 文章目录 前言: 自动注入: 自动注入 ...

  6. Spring官网阅读(一)容器及实例化

    从今天开始,我们一起过一遍Spring的官网,一边读,一边结合在路神课堂上学习的知识,讲一讲自己的理解.不管是之前关于动态代理的文章,还是读Spring的官网,都是为了之后对Spring的源码做更全面 ...

  7. Spring官网阅读(二)(依赖注入及方法注入)

    上篇文章我们学习了官网中的1.2,1.3两小节,主要是涉及了容器,以及Spring实例化对象的一些知识.这篇文章我们继续学习Spring官网,主要是针对1.4小节,主要涉及到Spring的依赖注入.虽 ...

  8. Spring官网阅读(九)Spring中Bean的生命周期(上)

    文章目录 生命周期回调 1.Bean初始化回调 2.Bean销毁回调 3.配置默认的初始化及销毁方法 4.执行顺序 5.容器启动或停止回调 Lifecycle 接口 LifecycleProcesso ...

  9. Spring官网阅读(十一)ApplicationContext详细介绍(上)

    文章目录 ApplicationContext 1.ApplicationContext的继承关系 2.ApplicationContext的功能 Spring中的国际化(MessageSource) ...

随机推荐

  1. python2.7安装pip

  2. 用python从0到1制作动态条形图的过程

    大家好,今天我们要讲的是如何使用Pyecharts制作动态排名变化图

  3. Leetcode802-找到最终的安全状态(Python3)

    刚开始没思路,还以为是利用二维矩阵直接标记节点间的有向路径,最后循环遍历就能得到结果,结果最后发现方向是错的,之后看了大佬们写的代码,发现原来是用出度来实现节点是否安全的. 照着大佬们的思路重新写了一 ...

  4. Jmeter接口测试、性能测试详细介绍

    下面主要就是讲一下Jmeter工具的用法,用法非常简单,比起loadrunner不知道简单多少,并且开源免费~~ 1.接口简介 接口定义 接口: 就是数据交互的入口和出口,是一套标准规范. 接口(硬件 ...

  5. 8. input限制手机输入

    1. 只能输入数字: <input id="num" type="number" value="0" onkeyup="va ...

  6. Mac安装多版本JDK

    0. 配置JDK环境 安装完成之后,配置.bash_profile文件 使用source ./bash_profile激活 2. 如何切换默认的jdk? 使用java -version就可以看默认版本 ...

  7. (第六篇)vim编辑器的使用

    什么是 vim(window文本文档) Vim是从 vi 发展出来的一个文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.简单的来说, vi 是老式的字处理器,不过功 ...

  8. 2019-2020-1 20199328《Linux内核原理与分析》第八周作业

    笔记部分 2019/11/4 17:55:22 elf文件代码默认加载到0x8048000,然后是一段首部信息,然后到达程序的真实入口 正常的系统调用会先进入内核态->用户态->系统调用下 ...

  9. Makefile 简要辅导 【转载】

    A Simple Makefile Tutorial Makefiles are a simple way to organize code compilation. This tutorial do ...

  10. Spring5参考指南:Bean的生命周期管理

    文章目录 Spring Bean 的生命周期回调 总结生命周期机制 startup和Shutdown回调 优雅的关闭Spring IoC容器 Spring Bean 的生命周期回调 Spring中的B ...