Spring 源码阅读-BeanDefinition 基本概念

Posted teago

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 源码阅读-BeanDefinition 基本概念相关的知识,希望对你有一定的参考价值。

在之前的文章中,主要从Spirng IOC、Bean的实例化以及Bean之间的关系来学习了Spring的相关知识。这篇文章主要从BeanDefinition的相关知识

1 Spring BeanDefinition简介

If you work with an ApplicationContext interface programmatically, child bean definitions are represented by the ChildBeanDefinition class. Most users do not work with them on this level. Instead, they configure bean definitions declaratively in a class such as the ClassPathXmlApplicationContext. When you use XML-based configuration metadata, you can indicate a child bean definition by using the parent attribute, specifying the parent bean as the value of this attribute. The following example shows how to do so:

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">  
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
</bean>

上面是来自Spring官网对于BeanDefinition的相描述。

总结来说:

  1. BeanDefinition包含了对bean做的配置,比如XML``标签的形式进行的配置
  2. Spring将对bean的定义信息进行了抽象,抽象后的实体就是BeanDefinition,并且Spring会以此作为标准来对Bean进行创建
  3. BeanDefinition包含以下元数据:
    • 一个全限定的类名,通常来说,就是对应的Bane的类名;
    • Bean的行为配置元素,这些元素展示了这个Bean在容器中是如何工作的包括scopelifecycle callbacks(生命周期回调)等等;
    • Bean的依赖信息;
    • 一些其它的相关配置信息。

比较正常创建一个Bean对象与通过Spring通过BeanDefinition创建一个Bean对象的不同:

  • 正常创建一个Java Bean
    技术图片

  • 通过BeanBeanDefinition来创建Bean
    技术图片

相对于正常的创建对象,Spring对其管理的bean没有直接采用new的方式,而是先通过解析配置数据以及根据对象本身的一些定义而获取其对应的Beandefinition,并将这个Beandefinition作为之后创建这个bean的依据。同时Spring在这个过程中提供了一些扩展点,例如在图中所提到了BeafactoryProcessor

2 BeanDefinition的方法分析

   // 获取父BeanDefinition,主要
   @Nullable
   String getParentName();
   // 设置bean的className
   void setBeanClassName(@Nullable String beanClassName);
   // 设置bean的作用域
   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);
   /**
    * Set a human-readable description of this bean definition.
    * @since 5.1
    */
   void setDescription(@Nullable String description);
   // 根据scope判断是否是单例
   boolean isSingleton();
   // 根据scope判断是否是原型
   boolean isPrototype();
   // 跟合并beanDefinition相关,如果是abstract,说明会被作为一个父beanDefinition,不用提供class属性
   boolean isAbstract();

}

BeanDefinition的继承关系
技术图片

2.1 BeanDefinition实现的接口
  • org.springframework.core.AttributeAccessor

技术图片

这个接口主要定义了元数据的访问接口。AbstractBeanDefinition是实现AttributeAccessor的抽象实现类之一。Spring通过这种方式,做到了数据接口与操作方法分离;接口中仅仅提哦那个了获取属性与设置属性的相关方法。而具体的数据实现则是在起实现类中去实现完成的;

  • org.springframework.beans.BeanMetadataElement

技术图片

这个接口只有一个获取资源对象的的方法。

当定义了一个SpringBean时,比如User ,这个时候可以理解为User对应的BeanDefinition通过GetSource来获取 到User.class 所对应的File对象;

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

2.2 AbstractBeanDefinition
  • org.springframework.core.AttributeAccessorSupport

AttributeAccessorSuppor实现了AttributeAccerror这个接口,AttributeAccerror实现了访问者的设计模式,将数据结构跟操作方法进行了分离;而数据结构就在AttributeAccessorSupport这个实现类中,其内部采用了LinkedHashMap实现。

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);
}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,而不是它的其它字段。

对比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;
........

这样做的好处是我们在创建其它子类的时候,会方便很多。

2.3 AbstractBeanDefinition的三个子类
  1. RootBeanDefinition

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

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

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

2.4 AnnotatedBeanDefinition

AnnotatedBeanDefinition实现了BeanDefinition接口,其源码仅仅只有两个方法,如下:

/**
 * Obtain the annotation metadata (as well as basic class metadata)
 * for this bean definition‘s bean class.
 * @return the annotation metadata object (never {@code null})
 */
AnnotationMetadata getMetadata();

/**
 * Obtain metadata for this bean definition‘s factory method, if any.
 * @return the factory method metadata, or {@code null} if none
 * @since 4.1.1
 */
@Nullable
MethodMetadata getFactoryMethodMetadata();
  • getMetadata(),主要用于获取注解元数据。主要用于保存通过注解方式定义bean所对应的BeanDefinition

    ,提供一个获取获取注解信息的方法;

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

2.5 AnnotatedBeanDefinition的三个实现类
  1. AnnotatedGenericBeanDefinition

    通过形如下面的API注册的bean都是AnnotatedGenericBeanDefinition

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.register(Config.class);
    }
    

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

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

  2. ScannedGenericBeanDefinition

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

  3. ConfigurationClassBeanDefinition

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

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

3 总结

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

  1. 什么是BeanDefinition,总结起来就是一句话,Spring创建bean时的建模对象。

  2. BeanDefinition的具体使用的子类,以及Spring在哪些地方使用到了它们。这部分内容在后面的学习中很重要,画图总结如下:

    技术图片

以上是关于Spring 源码阅读-BeanDefinition 基本概念的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码阅读:使用IDEA搭建Spring5.0.x源码阅读环境

Spring源码解析——如何阅读源码

Spring源码阅读 源码环境搭建

spring源码阅读笔记之HelloWorld和spring第一步ClassPathXMLApplicationContext

深入浅出Spring源码构建Spring源码阅读环境

深入浅出Spring源码构建Spring源码阅读环境