Spring(四)核心容器 - BeanDefinition 解析
前言
在上篇文章中,我们讨论了 refresh 的前四个方法,主要是对 ApplicationContext 上下文启动做一些准备工作。原计划是对接下来的 invokeBeanFactoryPostProcessors 方法进行讨论,但该方法涉及 Spring 中一个非常重要的概念: BeanDefinition,所以,这里先对 BeanDefinition 进行讨论,这样也有利于完善 Spring 的知识体系。
注:本篇文章使用的 SpringBoot 版本为 2.0.3.RELEASE,其 Spring 版本为 5.0.7.RELEASE
正文
现如今,我们一般获取对象的方式有两种,一种是手动直接 new;另一种是交给 Spring 管理,Spring 将管理的对象称之为 Bean,容器会先实例化 Bean,然后自动注入,实例化的过程就需要依赖 BeanDefinition。
BeanDefinition 用于保存 Bean 的相关信息,包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载等,它是实例化 Bean 的原材料,Spring 就是根据 BeanDefinition 中的信息实例化 Bean。
BeanDefinition的继承体系
BeanDefinition 是一个接口,它有多个实现类,这些实现类分别描述不同类型的 Bean。
BeanDefinition
一个 BeanDefinition 描述了一个 Bean 实例,实例包含属性值、构造方法参数值以及更多实现信息。该 BeanDefinition 只是是一个最小的接口,主要目的是允许修改属性值和其他 Bean 元数据,这里列出几个核心方法。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 单例、原型标识符
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 标识 Bean 的类别,分别对应 用户定义的 Bean、来源于配置文件的 Bean、Spring 内部的 Bean
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 设置、返回 Bean 的父类名称
void setParentName(@Nullable String parentName);
String getParentName();
// 设置、返回 Bean 的 className
void setBeanClassName(@Nullable String beanClassName);
String getBeanClassName();
// 设置、返回 Bean 的作用域
void setScope(@Nullable String scope);
String getScope();
// 设置、返回 Bean 是否懒加载
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 设置、返回当前 Bean 所依赖的其它 Bean 名称。
void setDependsOn(@Nullable String... dependsOn);
String[] getDependsOn();
// 设置、返回 Bean 是否可以自动注入。只对 @Autowired 注解有效
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
// 设置、返回当前 Bean 是否为主要候选 Bean 。
// 当同一个接口有多个实现类时,通过该属性来配置某个 Bean 为主候选 Bean。
void setPrimary(boolean primary);
boolean isPrimary();
// 设置、返回创建该 Bean 的工厂类。
void setFactoryBeanName(@Nullable String factoryBeanName);
String getFactoryBeanName();
// 设置、返回创建该 Bean 的工厂方法
void setFactoryMethodName(@Nullable String factoryMethodName);
String getFactoryMethodName();
// 返回该 Bean 构造方法参数值、所有属性
ConstructorArgumentValues getConstructorArgumentValues();
MutablePropertyValues getPropertyValues();
// 返回该 Bean 是否是单例、是否是非单例、是否是抽象的
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
// 返回 Bean 的类别。类别对应上面的三个属性值。
int getRole();
...
}
可以看到 BeanDefinition 接口提供了一系列操作 Bean 元数据的set、get方法,这些操作为 Bean 的描述定义了一套模板,具体的实现则交由子类。
AnnotatedBeanDefinition
AnnotatedBeanDefinition 是 BeanDefinition 子接口之一,该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据。一般情况下,通过注解方式得到的 Bean(@Component、@Bean),其 BeanDefinition 类型都是该接口的实现类。
public interface AnnotatedBeanDefinition extends BeanDefinition {
// 获得当前 Bean 的注解元数据
AnnotationMetadata getMetadata();
// 获得当前 Bean 的工厂方法上的元数据
MethodMetadata getFactoryMethodMetadata();
}
该接口可以返回两个元数据的类:
AnnotationMetadata:主要对 Bean 的注解信息进行操作,如:获取当前 Bean 标注的所有注解、判断是否包含指定注解。
MethodMetadata:方法的元数据类。提供获取方法名称、此方法所属类的全类名、是否是抽象方法、判断是否是静态方法、判断是否是final方法等。
AbstractBeanDefinition
AbstractBeanDefinition 是 BeanDefinition 的子抽象类,也是其他 BeanDefinition 类型的基类,其实现了接口中定义的一系列操作方法,并定义了一系列的常量属性,这些常量会直接影响到 Spring 实例化 Bean 时的策略。核心属性如下。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// 默认的 SCOPE,默认是单例
public static final String SCOPE_DEFAULT = "";
// 不进行自动装配
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
// 根据 Bean 的名字进行自动装配,byName
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
// 根据 Bean 的类型进行自动装配,byType
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
// 根据构造器进行自动装配
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
// 首先尝试按构造器自动装配。如果失败,再尝试使用 byType 进行自动装配。(Spring 3.0 之后已废除)
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
// 通过依赖检查来查看 Bean 的每个属性是否都设置完成
// 以下常量分别对应:不检查、对依赖对象检查、对基本类型,字符串和集合进行检查、对全部属性进行检查
public static final int DEPENDENCY_CHECK_NONE = 0;
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
public static final int DEPENDENCY_CHECK_ALL = 3;
// 关闭应用上下文时需调用的方法名称
public static final String INFER_METHOD = "(inferred)";
// 存放 Bean 的 Class 对象
private volatile Object beanClass;
// Bean 的作用范围
private String scope = SCOPE_DEFAULT;
// 非抽象
private boolean abstractFlag = false;
// 非延迟加载
private boolean lazyInit = false;
// 默认不自动装配
private int autowireMode = AUTOWIRE_NO;
// 默认不依赖检查
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
// 依赖的 Bean 列表
private String[] dependsOn;
// 可以作为自动装配的候选者,意味着可以自动装配到其他 Bean 的某个属性中
private boolean autowireCandidate = true;
// 创建当前 Bean 实例工厂类名称
private String factoryBeanName;
// 创建当前 Bean 实例工厂类中方法名称
private String factoryMethodName;
// 存储构造方法的参数
private ConstructorArgumentValues constructorArgumentValues;
// 存储 Bean 属性名称以及对应的值
private MutablePropertyValues propertyValues;
// 存储被覆盖的方法信息
private MethodOverrides methodOverrides;
// init、destroy 方法名称
private String initMethodName;
private String destroyMethodName;
// 是否执行 init 和 destroy 方法
private boolean enforceInitMethod = true;
private boolean enforceDestroyMethod = true;
// Bean 是否是用户定义的而不是应用程序本身定义的
private boolean synthetic = false;
// Bean 的身份类别,默认是用户定义的 Bean
private int role = BeanDefinition.ROLE_APPLICATION;
// Bean 的描述信息
private String description;
// Bean 定义的资源
private Resource resource;
...
}
以上是 AbstractBeanDefinition 中定义的一些常量和属性,该类中还有一部分是操作这些属性的 set 和 get 方法,这些方法都由子类来操作,且应用程序中真正使用的也是这些子类 BeanDefinition。
先来看 AbstractBeanDefinition 直接实现类:RootBeanDefinition、GenericBeanDefinition、ChildBeanDefinition。
RootBeanDefinition
该类继承自 AbstractBeanDefinition,它可以单独作为一个 BeanDefinition,也可以作为其他 BeanDefinition 的父类。
RootBeanDefinition 在 AbstractBeanDefinition 的基础上定义了更多属性。
public class RootBeanDefinition extends AbstractBeanDefinition {
// BeanDefinitionHolder 存储 Bean 的名称、别名、BeanDefinition
private BeanDefinitionHolder decoratedDefinition;
// AnnotatedElement 是java反射包的接口,通过它可以查看 Bean 的注解信息
private AnnotatedElement qualifiedElement;
// 允许缓存
boolean allowCaching = true;
// 工厂方法是否唯一
boolean isFactoryMethodUnique = false;
// 封装了 java.lang.reflect.Type,提供了泛型相关的操作
volatile ResolvableType targetType;
// 缓存 Class,表示 RootBeanDefinition 存储哪个类的信息
volatile Class<?> resolvedTargetType;
// 缓存工厂方法的返回类型
volatile ResolvableType factoryMethodReturnType;
// 这是以下四个构造方法字段的通用锁
final Object constructorArgumentLock = new Object();
// 用于缓存已解析的构造方法或工厂方法
Executable resolvedConstructorOrFactoryMethod;
// 将构造方法参数标记为已解析
boolean constructorArgumentsResolved = false;
// 用于缓存完全解析的构造方法参数
Object[] resolvedConstructorArguments;
// 缓存待解析的构造方法参数
Object[] preparedConstructorArguments;
// 这是以下两个后处理字段的通用锁
final Object postProcessingLock = new Object();
// 表明是否被 MergedBeanDefinitionPostProcessor 处理过
boolean postProcessed = false;
// 在生成代理的时候会使用,表明是否已经生成代理
volatile Boolean beforeInstantiationResolved;
// 实际缓存的类型是 Constructor、Field、Method 类型
private Set<Member> externallyManagedConfigMembers;
// InitializingBean中 的 init 回调函数名 afterPropertiesSet 会在这里记录,以便进行生命周期回调
private Set<String> externallyManagedInitMethods;
// DisposableBean 的 destroy 回调函数名 destroy 会在这里记录,以便进生命周期回调
private Set<String> externallyManagedDestroyMethods;
...
}
ChildBeanDefinition
该类继承自 AbstractBeanDefinition。其相当于一个子类,不可以单独存在,必须依赖一个父 BeanDetintion,构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。它可以从父类继承方法参数、属性值,并可以重写父类的方法,同时也可以增加新的属性或者方法。若重新定义 init 方法,destroy 方法或者静态工厂方法,ChildBeanDefinition 会重写父类的设置。
从 Spring 2.5 开始,以编程方式注册 Bean 定义的首选方法是 GenericBeanDefinition,GenericBeanDefinition 可以有效替代 ChildBeanDefinition 的绝大分部使用场合。
GenericBeanDefinition
GenericBeanDefinition 是 Spring 2.5 以后新引入的 BeanDefinition,是 ChildBeanDefinition 更好的替代者,它同样可以通过 setParentName 方法设置父 BeanDefinition。
最后三个 BeanDefinition 既实现了 AnnotatedBeanDefinition 接口,又间接继承 AbstractBeanDefinition 抽象类,这些 BeanDefinition 描述的都是注解形式的 Bean。
ConfigurationClassBeanDefinition
该类继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述在标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean。
其功能特点如下:
1、如果 @Bean 注解没有指定 Bean 的名字,默认会用方法的名字命名 Bean。
2、标注 @Configuration 注解的类会成为一个工厂类,而标注 @Bean 注解的方法会成为工厂方法,通过工厂方法实例化 Bean,而不是直接通过构造方法初始化。
3、标注 @Bean 注解的类会使用构造方法自动装配
AnnotatedGenericBeanDefinition
该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Configuration 注解的 Bean。
ScannedGenericBeanDefinition
该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service、@Controller 也同理。
总结
最后,我们来做个总结。BeanDefinition 主要是用来描述 Bean,其存储了 Bean 的相关信息,Spring 实例化 Bean 时需读取该 Bean 对应的 BeanDefinition。BeanDefinition 整体可以分为两类,一类是描述通用的 Bean,还有一类是描述注解形式的 Bean。一般前者在 XML 时期定义 <bean‘> 标签以及在 Spring 内部使用较多,而现今我们大都使用后者,通过注解形式加载 Bean。
以上就是本章内容,如果文章中有错误或者需要补充的请及时提出,本人感激不尽。
参考:
http://cmsblogs.com/?p=11731
https://cloud.tencent.com/developer/article/1497805
Spring(四)核心容器 - BeanDefinition 解析的更多相关文章
- Spring之核心容器bean
摘要:Spring的核心容器实现了Ioc,其目 的是提供一种无侵入式的框架.在本文中,首先讲解了Spring的基础bean的相关知识,然后介绍了Spring是如何对bean进行管理的. 在Spring ...
- 【Spring】 Spring的核心容器
Spring的核心容器 文章目录 Spring的核心容器 BeanFactory ApplicationContext 1.通过ClassPathXmlApplicationContext创建 2.通 ...
- Spring的核心容器
Spring框架的主要功能是通过其核心容器来实现的.Spring提供了2种核心容器:BeanFactory.ApplicationContext. BeanFactory BeanFactory是一个 ...
- spring的核心容器ApplicationContext
//bean.xml配置文件 <?xml version="1.0" encoding="UTF-8"?><beans xmlns=" ...
- Spring学习总结(6)——Spring之核心容器bean
一.Bean的基础知识 1.在xml配置文件中,bean的标识(id 和 name) id:指定在benafactory中管理该bean的唯一的标识.name可用来唯一标识bean 或给bean起别名 ...
- 初识Spring——Spring核心容器
一. IOC和DI基础 IOC-Inversion of Control,译为控制反转,是一种遵循依赖倒置原则的代码设计思想. 所谓依赖倒置,就是把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑 ...
- spring技术核心概念纪要
一.背景 springframework 从最初的2.5版本发展至今,期间已经发生了非常多的修正及优化.许多新特性及模块的出现,使得整个框架体系显得越趋庞大,同时也带来了学习及理解上的困难. 本文阐述 ...
- 一头扎进Spring之---------Spring核心容器----------
1.什么是 IOC/DI? IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创建.依赖的代码,反转给容器来帮忙实现.那么必然的我们需要创建 ...
- Spring IOC 低级容器解析
1.IOC是什么 IOC-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不 ...
随机推荐
- 数据库基础之Mysql
数据库的简介 数据库 数据库(database,DB)是指长期存储在计算机内的,有组织,可共享的数据的集合.数据库中的数据按一定的数学模型组织.描述和存储,具有较小的冗余,较高的数据独立性和易扩展性, ...
- redis集群数据迁移
redis集群数据备份迁移方案 n 迁移环境描述及分析 当前我们面临的数据迁移环境是:集群->集群. 源集群: 源集群为6节点,3主3备 主 备 192.168.112.33:8001 192 ...
- $vjudge\ CSP-S$专题专练题解
照例先放个链接$QwQ$ $A$ $QwQ$之前写过题解辣. 重新说下趴,就给横坐标纵坐标也开点,然后每个点连向对应横纵坐标边权为$0$,相邻横坐标点之间连边,相邻纵坐标点之间连边,跑个最短路就完事$ ...
- Linux 查看实时网卡流量的方法 sar nload iftop
1.sar 计量脚本 sar(System Activity Reporter 系统活动情况报告)是目前 Linux 上最为全面的系统性能分析工具之一,可以从多方面对系统的活动进行报告,包括:文件的读 ...
- MacBook Pro 入手一年了,到底香不香?
最近又有小伙伴问到底值不值得入手一台 MacBook Pro,松哥自己在 2018 年 10 月份的时候入手了一台,到现在为止,也用了一年多了,今天就来和小伙伴们聊一聊使用感受,至于到底值不值,需要大 ...
- 手把手带你了解消息中间件(3)——RocketMQ
一.RocketMQ简介 RocketMQ作为一款纯java.分布式.队列模型的开源消息中间件,支持事务消息.顺序消息.批量消息.定时消息.消息回溯等. 二.RocketMQ架构 如图所示为R ...
- 微信小程序之双重循环(包含左滑删除,以及数据各项处理)
<view wx:for="{{hommer}}" wx:for-item="item" wx:for-index="index" w ...
- vnpy源码阅读学习(2):学习PyQt5
PyQt5的学习 花费了一个下午把PyQt5大概的学习了下.找了一个教程 PyQt5教程 跟着挨着把上面的案例做了一遍,大概知道PyQt5是如何生成窗体,以及控件的.基本上做到如果有需求要实现,查查手 ...
- Django框架初体验
前言 从今天开始学习测试开发知识,并会把每一次学习的过程和成果记录到博客,由于我也没怎么接触过python相关的开发框架,所以前期应该是艰难的,但是我相信努力就会有收获,如果你和我一样是个小白,那我们 ...
- APICloud打开三方地图整合
一直想系统的整理打开地图的方法,今天抽时间把了百度,高德,腾讯,苹果自带地图都整理出来了,闲话不多说,直接上干货 ------------------------------------------- ...