Spring官网阅读(四)BeanDefinition(上)
前面几篇文章已经学习了官网中的
1.2
,1.3
,1.4
三小结,主要是容器,Bean的实例化及Bean之间的依赖关系等。这篇文章,我们继续官网的学习,主要是BeanDefinition
的相关知识,这是Spring中非常基础的一块内容,也是我们阅读源码的基石。本文主要涉及到官网中的1.3
及1.5
中的一些补充知识。同时为我们1.7
小节中BeanDefinition
的合并做一些铺垫
文章目录
- BeanDefinition是什么?
- BeanDefinition的方法分析
- BeanDefinition的继承关系
- 1.BeanDefinition继承的接口
- 2.AbstractBeanDefinition
- AbstractBeanDefinition的继承关系
- 为什么需要AbstractBeanDefinition?
- 3.AbstractBeanDefinition的三个子类
- GenericBeanDefinition
- ChildBeanDefinition
- RootBeanDefinition
- 4.AnnotatedBeanDefinition
- 5.AnnotatedBeanDefinition的三个实现类
- AnnotatedGenericBeanDefinition
- ScannedGenericBeanDefinition
- ConfigurationClassBeanDefinition
- 总结
- 1.5小结内容的补充
- 单例
- 原型
BeanDefinition是什么?
我们先看官网上是怎么解释的:
从上文中,我们可以得出以下几点结论:
BeanDefinition
包含了我们对bean做的配置,比如XML<bean/>
标签的形式进行的配置- 换而言之,Spring将我们对bean的定义信息进行了抽象,抽象后的实体就是
BeanDefinition
,并且Spring会以此作为标准来对Bean进行创建 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<>();@Overridepublic 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
的源码我们可以发现,AbstractBeanDefinition
对BeanDefinition
的大部分方法做了实现(没有实现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
合并的相关知识。这篇文章中,主要学习了
- 什么是
BeanDefinition
,总结起来就是一句话,Spring创建bean时的建模对象。 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(上)相关推荐
- Spring官网阅读 | 总结篇
接近用了4个多月的时间,完成了整个<Spring官网阅读>系列的文章,本文主要对本系列所有的文章做一个总结,同时也将所有的目录汇总成一篇文章方便各位读者来阅读. 下面这张图是我整个的写作大 ...
- Spring官网阅读(三)自动注入
上篇文章我们已经学习了1.4小结中关于依赖注入跟方法注入的内容.这篇文章我们继续学习这结中的其他内容,顺便解决下我们上篇文章留下来的一个问题-----注入模型. 前言: 在看下面的内容之前,我们先要对 ...
- Spring官网阅读(一)容器及实例化
从今天开始,我们一起过一遍Spring的官网,为Spring源码的学习打好基础.在这个过程中,不会涉及过多底层的代码,更多是通过例子证明我们在官网得出的结论,希望自己可以坚持下来,给自己加个油!!! ...
- Spring官网阅读(二)(依赖注入及方法注入)
上篇文章我们学习了官网中的1.2,1.3两小节,主要是涉及了容器,以及Spring实例化对象的一些知识.这篇文章我们继续学习Spring官网,主要是针对1.4小节,主要涉及到Spring的依赖注入.虽 ...
- Spring 官网阅读指南
一.概述 Spring官网:https://spring.io/,界面如下: 二.各模块介绍 进入首页如上图,在首页官网会展示一些当前Spring比较流行的技术.可以看见导航栏有几个模块,信息如下: ...
- Spring官网阅读(九)Spring中Bean的生命周期(上)
文章目录 生命周期回调 1.Bean初始化回调 2.Bean销毁回调 3.配置默认的初始化及销毁方法 4.执行顺序 5.容器启动或停止回调 Lifecycle 接口 LifecycleProcesso ...
- Spring官网和源码系列-如何阅读
作为一名Java从业者,有听过这么一句话,Spring源码至少看三遍才能算是一名软件工程师.对于读源码,大家就觉得很头疼,潜意识里感觉枯燥.难,基本就是能不读就不读,工作中够用就行了.不读源码的确可以 ...
- Spring官网改版后下载方式
2019独角兽企业重金招聘Python工程师标准>>> Spring官网改版后,很多项目的完整zip包下载链接已经隐掉了,虽然Spring旨在引导大家用更"高大上" ...
- Spring官网下载dist.zip的几种方法
Spring官网改版后,很多项目的完整zip包下载链接已经隐掉了,虽然Spring旨在引导大家用更"高大上"的maven方式来管理所依赖的jar包,但是完全没想到中国的国情,在伟大 ...
最新文章
- 第九章 硬件抽象层:HAL 心得笔记
- 让该死的恶意软件去死吧!!!!!!!
- python 图片打印文章总结
- 算法全覆盖,还能玩星际争霸,开源决策智能平台OpenDILab面世
- 代画PCB及C语言编程
- vbmsgbox引用变量_vba中MsgBox的参数及用法
- java owl文件_jena解析.owl文件 实现owl文件的解析 - 下载 - 搜珍网
- PHP|Yii2下通过插件进行图片处理(亲测可用)
- Oracle ITL(Interested Transaction List)理解
- oracle如何设置权限,Oracle创建用户并设置权限
- 选择爱人的数学方法(经典秘书问题)
- 做高频通用还是低频专业
- web.config 加密/解密
- tf卡可以自己裁剪成nm卡_这些年Surface 3用过的TF卡与购买心得
- MATLAB一元微积分实验
- 移动营业厅前台设备如何安装+新手引导
- IMO 双帐号同步API文档
- STM32和51最小系统原理图以及PCB板图以及元件库
- 20天学会UI设计(PS+AI)入门教程
- QUESTION: 由于文件 无法被用户‘_apt‘访问,已脱离沙盒并提权为根用户来进行下载。 - pkgAcquire::Run (13: 权限不够)
热门文章
- ffmpeg+easydarwin把rtmp流转换成m3u8
- std::thread与pthread
- C#根据出生日期计算年龄的源码
- Oracle bpm实现oa,Oracle BPM/SOA API 操作流程
- 听闻华为停止社招,为什么我会感到一丝恐慌
- 将Outlook中的邮件保存到本地磁盘,释放邮箱空间
- 红帽子linux网络播放器,redhat默认播放器(totem)解码包安装
- 基于滴滴云搭 SeaweedFS 存储系统
- 休眠(Hibernate)和睡眠(Sleep)的区别
- Kubernetes kube-proxy 如何与 iptables 完美配合使用