Spring官网阅读(四)BeanDefinition(上)

点击上方蓝字java

给一个关注吧程序员


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

  • 1.BeanDefinition继承的接口spring

  • 2.AbstractBeanDefinition微信

    • AbstractBeanDefinition的继承关系数据结构

    • 为何须要AbstractBeanDefinition?编辑器

  • 3.AbstractBeanDefinition的三个子类ide

    • GenericBeanDefinition函数

    • ChildBeanDefinition学习

    • RootBeanDefinition

    • 4.AnnotatedBeanDefinition

    • 5.AnnotatedBeanDefinition的三个实现类

  • 1.5小结内容的补充

    • 单例

    • 原型

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 AttributeAccessorSerializable {
 /** 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{
    
}
您点的每一个赞,我都认真当成了喜欢



本文分享自微信公众号 - 程序员DMZ(programerDmz)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索