SpringBoot源码解读与原理分析(十一)BeanDefinition

发布时间:2024年01月16日

3.5 BeanDefinition

顾名思义,BeanDefinition是指Bean的定义信息。这个类的设计相当重要,对理解SpringBoot的扩展点、生命周期等有很大帮助。

3.5.1 BeanDefinition概述

BeanDefinition的javadoc并没有提供有价值的信息,但可以从BeanDefinition接口定义的方法入手,来看BeanDefinition包含了哪些定义信息。

1.Bean的类信息

  • 全限定名(beanClassName)
void setBeanClassName(@Nullable String beanClassName);
String getBeanClassName();

2.Bean的属性

  • 作用域(scope)
void setScope(@Nullable String scope);
String getScope();
  • 是否默认Bean(primary)
void setPrimary(boolean primary);
boolean isPrimary();
  • 描述信息(description)
void setDescription(@Nullable String description);
String getDescription();

3.Bean的行为特性

  • 是否延迟加载(lazyInit)
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
  • 是否自动注入(autowireCandidate)
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
  • 初始化方法(initMethodName)
void setInitMethodName(@Nullable String initMethodName);
String getInitMethodName();
  • 销毁方法(destroyMethodName)
void setDestroyMethodName(@Nullable String destroyMethodName);
String getDestroyMethodName();

4.Bean与其他Bean的关系

  • 父Bean名称(parentName)
void setParentName(@Nullable String parentName);
String getParentName();
  • 依赖的Bean(dependsOn)
void setDependsOn(@Nullable String... dependsOn);
String[] getDependsOn();

5.Bean的配置属性

  • 构造方法参数(constructorArgumentValues)
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
    return !getConstructorArgumentValues().isEmpty();
}
  • 属性变量值(propertyValues)
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
    return !getPropertyValues().isEmpty();
}

以上并没有把全部方法列出来,但足以看出BeanDefinition几乎已经把Bean的所有信息都收集并封装起来了。

3.5.3 BeanDefinition的结构与设计

借助IDEA,生成BeanDefinition接口的上下级继承和派生关系:
BeanDefinition接口的上下级继承和派生关系
可以发现,BeanDefinition并不是顶层接口,它的上层有一些父接口,下层也有一些扩展实现。

3.5.3.1 AttributeAccessor

AttributeAccessor是BeanDefinition的父接口,由类名可知它是一个属性访问器,用于获取属性值、写入属性值、移除属性、判断是否存在某个属性。

public interface AttributeAccessor {

    // 设置bean对象中属性的值
    void setAttribute(String name, @Nullable Object value);
    
    // 获取bean对象中指定属性的值
    Object getAttribute(String name);
    
    // 移除bean对象中指定属性
    Object removeAttribute(String name);
    
    // 判断bean对象中是否存在指定的属性
    boolean hasAttribute(String name);
    
    // 获取bean对象中所有的属性
    String[] attributeNames();
    
}

BeanDefinition接口继承自AttributeAccessor,因此具有配置Bean属性(访问、修改、移除等)的功能。

3.5.3.2 BeanMetadataElement

BeanMetadataElement是BeanDefinition的另一个父接口,类名直译为存放Bean的元数据的元素。这个接口只有一个方法:

public interface BeanMetadataElement {
    @Nullable
    default Object getSource() {
        return null;
    }
}

这个方法用于获取Bean的资源来源,也即Bean的文件路径。一般情况下,项目中所编写的所有注册到IOC容器的Bean,都是从本地磁盘上的.class文件加载进来的,所以此处获取的就是这个.class文件(Resource对象或者File对象)。

3.5.3.3 AbstractBeanDefinition

AbstractBeanDefinition是BeanDefinition接口的基本抽象实现,定义了部分属性和逻辑实现。AbstractBeanDefinition重写BeanDefinition中定义的方法并操作这些属性。

// Bean的全限定类名
private volatile Object beanClass;

// 默认作用域,""与singleton等价,也就是默认单实例
public static final String SCOPE_DEFAULT = "";
private String scope = SCOPE_DEFAULT;

// 默认Bean都不是抽象的
private boolean abstractFlag = false;

// 是否延迟初始化
private Boolean lazyInit;

// 默认不是首选Bean
private boolean primary = false;

// Bean的构造方法参数和参数值列表
private ConstructorArgumentValues constructorArgumentValues;

// Bean的属性和属性值集合
private MutablePropertyValues propertyValues;

// Bean的初始化方法和销毁方法
private String initMethodName;
private String destroyMethodName;

// Bean的资料来源
private Resource resource;

// ...

Base class for concrete, full-fledged BeanDefinitionclasses, factoring out common properties of GenericBeanDefinition, RootBeanDefinition, and ChildBeanDefinition.

AbstractBeanDefinition是具体的、完整的BeanDefinition的基类,分解出GenericBeanDefinition、RootBeanDefinition、ChildBeanDefinition的公共属性。

从javadoc得知,AbstractBeanDefinition中定义的属性是一些公共属性,意味着不同的BeanDefinition落地实现,内部的属性还是有差异的。

3.5.3.4 GenericBeanDefinition

GenericBeanDefinition是BeanDefinition的其中一个落地实现类。Generic表示“通用的”“一般的”,因此GenericBeanDefinition具有一般性。

GenericBeanDefinition的源码非常简单,在继承AbstractBeanDefinition类之后,只定义了一个属性:parentName。

public class GenericBeanDefinition extends AbstractBeanDefinition {
    
    private String parentName;
    // ...
}

GenericBeanDefinition is a one-stop shop for standard bean definition purposes. Like any bean definition, it allows for specifying a class plus optionally constructor argument values and property values. Additionally, deriving from a parent bean definition can be flexibly configured through the “parentName” property.

第一段:GenericBeanDefinition是实现标准Bean定义的一站式服务。和任何Bean定义一样,它允许指定的类和可选的构造函数参数值和属性值。此外,可以通过parentName属性灵活地配置父级Bean定义。

In general, use this GenericBeanDefinitionclass for the purpose of registering user-visible bean definitions (which a post-processor might operate on, potentially even reconfiguring the parent name). Use RootBeanDefinition/ ChildBeanDefinitionwhere parent/child relationships happen to be pre-determined.

第二段:通常,使用GenericBeanDefinition类的目的是注册用户可见的Bean定义(后处理器可能会对其操作,甚至可能重新配置父级名称)。在父子关系预先确定的地方使用RootBeanDefinition和ChildBeanDefinition。

由此可见,GenericBeanDefinition是通用的BeanDefinition落地实现,并且具有层次性

3.5.3.5 ChildBeanDefinition

ChildBeanDefinition从类名来看,是指子定义信息。因此在继承AbstractBeanDefinition的同时,也带有parentName属性,和GenericBeanDefinition几乎是一模一样的。

在javadoc中描述了ChildBeanDefinition和GenericBeanDefinition的区别:

NOTE: Since Spring 2.5, the preferred way to register bean definitions programmatically is the GenericBeanDefinitionclass, which allows to dynamically define parent dependencies through the setParentNamemethod. This effectively supersedes the ChildBeanDefinition class for most use cases.

自SpringFramework 2.5以来,以编程方式注册Bean定义的首选方法是GenericBeanDefinition类,它允许通过setParentName方法动态定义父依赖项。在大多数用例中,这有效地替代了ChildBeanDefinition类。

可见,官方是推荐使用GenericBeanDefinition的。

在源码上,两者也有一些不一样:GenericBeanDefinition有不带参数的构造方法,而ChildBeanDefinition全部构造方法都带parentName参数,这样做是因为ChildBeanDefinition是在父子关系预先确定的地方使用的。

3.5.3.6 RootBeanDefinition

RootBeanDefinition的类名中有“根”的概念,这就意味着RootBeanDefinition只能作为单独的BeanDefinition或者父级BeanDefinition出现。

从源码得知,RootBeanDefinition在AbstractBeanDefinition的基础上,扩展了一些Bean的其他信息:

// BeanDefinition的引用持有,存放了Bean的别名
private BeanDefinitionHolder decoratedDefinition;
// Bean上面的注解信息
private AnnotatedElement qualifiedElement;
// Bean中的泛型
volatile ResolvableType targetType;
// BeanDefinition对应的真实Bean
volatile Class<?> resolvedTargetType;
// 是否是FactoryBean
volatile Boolean isFactoryBean;
//工厂Bean方法返回的类型
volatile ResolvableType factoryMethodReturnType;
// ...

扩展的信息包括:id和别名、注解信息、工厂相关信息(是否为工厂Bean等等)。可见,RootBeanDefinition在底层做了更多的事情。(至于做了什么事情,暂时没有深入研究,后续做补充)

3.5.3.7 小结

至此,可以对BeanDefinition的结构与设计做个小结:

  • BeanDefinition接口定义了属性操作方法规范,AbstractBeanDefinition抽象实现类具体定义了这些属性以及重写属性操作方法。
  • GenericBeanDefinition、ChildBeanDefinition、RootBeanDefinition是三种落地实现,三者之间有细微差别,其中GenericBeanDefinition、ChildBeanDefinition主要实现层次性,RootBeanDefinition扩展了部分属性。
  • BeanDefinition的上层是AttributeAccessor和BeanMetadataElement,分别用于属性访问和存储元数据。

3.5.4 BeanDefinition实例

通过两个简单的例子,直观感受BeanDefinition的设计,以及其中封装的属性。

3.5.4.1 基于组件扫描的BeanDefinition

使用模式注解+组件扫描的方式,每扫描到一个类,就相当于构建了一个BeanDefinition。

(1)定义一个Person类,并标注@Component:

@Component
public class Person {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

(2)使用AnnotationConfigApplicationContext扫描指定包路径下标注了@Component直接的组件:

public class DemoApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.star.springboot.test03");
        BeanDefinition beanDefinition = context.getBeanDefinition("person");
        System.out.println(beanDefinition);
    }

}

(3)打印BeanDefinition信息:

Generic bean: class [com.star.springboot.test03.Person]; 
scope=singleton; abstract=false; lazyInit=null; autowireMode=0; 
dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null;
factoryMethodName=null; initMethodName=null; destroyMethodName=null; 
defined in file [D:\learnspace\workspace\java_src\springboot-demo\springboot-03-ioc\target\classes\com\star\springboot\test03\Person.class]

由结果可知,这是一个GenericBeanDefinition,且定义来源在类的.class文件中。

3.5.4.1 基于@Bean的BeanDefinition

(1)编写注册了Person的注解配置类

@Configuration
public class PersonConfiguration {

    @Bean
    public Person person2222() {
        return new Person();
    }

}

(2)使用AnnotationConfigApplicationContext注册该注解配置类(和上例进行比较):

public class DemoApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.star.springboot.test03");
        BeanDefinition beanDefinition = context.getBeanDefinition("person");
        System.out.println(beanDefinition);
        System.out.println("=======================");
        AnnotationConfigApplicationContext context2 = new AnnotationConfigApplicationContext(PersonConfiguration.class);
        BeanDefinition beanDefinition2 = context2.getBeanDefinition("person2222");
        System.out.println(beanDefinition2);
    }

}

(3)打印BeanDefinition信息:

Generic bean: class [com.star.springboot.test03.Person]; 
scope=singleton; abstract=false; lazyInit=null; autowireMode=0; 
dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null;
factoryMethodName=null; initMethodName=null; destroyMethodName=null; 
defined in file [D:\learnspace\workspace\java_src\springboot-demo\springboot-03-ioc\target\classes\com\star\springboot\test03\Person.class]
=======================
Root bean: class [null]; 
scope=; abstract=false; lazyInit=null; autowireMode=3; 
dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=personConfiguration; 
factoryMethodName=person2222; initMethodName=null; destroyMethodName=(inferred); 
defined in com.star.springboot.test03.PersonConfiguration

由结果可知,两种方式获取的BeanDefinition区别很大:

基于组件扫描基于@Bean
BeanDefinition类型Generic beanRoot bean
className[com.star.springboot.test03.Person][null]
scopesingleton
autowireMode03
factoryBeanNamenullpersonConfiguration
factoryMethodNamenullperson2222
destroyMethodNamenull(inferred)
defined infile[Person.class]PersonConfiguration

为什么两种不同的Bean定义方式在实际运行时会有如此大的差别呢?通过查看源码可以大致梳理出其逻辑:

  • 通过模式注解+组件扫描方式构造的BeanDefinition,它的扫描工具是ClassPathBeanDefinitionScanner,扫描器会扫描指定包路径下包含特定模式注解的类,核心扫描方法是doScan,它会调用父类ClassPathScanningCandidateComponentProvider的findCandidateComponents方法,创建ScannedGenericBeanDefinition并返回。

ClassPathBeanDefinitionScanner.java

public int scan(String... basePackages) {
    // ...
    // 调用核心扫描方法是doScan
    doScan(basePackages);
    // ...
}

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // ...
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 调用父类findCandidateComponents方法
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // ...
        }
    }
    return beanDefinitions;
}

ClassPathScanningCandidateComponentProvider.java

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    } else {
        return scanCandidateComponents(basePackage);
    }
}

private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    // ...
    // 创建ScannedGenericBeanDefinition并返回
    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    // ...
    candidates.add(sbd);
    // ...
    return candidates;
}
  • 基于配置类+@Bean注解的方式构造的BeanDefinition,配置类的解析工具是ConfigurationClassPostProcessor,核心解析方法是processConfigBeanDefinitions,该方法会处理@Configuration配置类,并交给ConfigurationClassParser来解析配置类,提取出所有标注了@Bean的方法。随后这些方法又被ConfigurationClassBeanDefinitionReader解析,最终在底层创建ConfigurationClassBeanDefinition并返回。

AnnotationConfigApplicationContext.java

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    // 入口
    refresh();
}

AbstractApplicationContext.java

public void refresh() throws BeansException, IllegalStateException {
    // ...
    // Invoke factory processors registered as beans in the context.
    invokeBeanFactoryPostProcessors(beanFactory);
    // ...
}

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    // ...
}

PostProcessorRegistrationDelegate.java

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    // ...
    BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
    registryProcessor.postProcessBeanDefinitionRegistry(registry);
    // ...
}

ConfigurationClassPostProcessor.java

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // ...
    processConfigBeanDefinitions(registry);
}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // ...
    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    // ...
    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
        this.reader = new ConfigurationClassBeanDefinitionReader(
            registry, this.sourceExtractor, this.resourceLoader, this.environment,
            this.importBeanNameGenerator, parser.getImportRegistry());
    }
    this.reader.loadBeanDefinitions(configClasses);
    //...
}

ConfigurationClassBeanDefinitionReader.java

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    // ...
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        // 为@Bean方法加载BeanDefinition
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    // ...
}

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    // ...
    // 创建ConfigurationClassBeanDefinition对象并返回
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
    // ...
}

3.5.5 BeanDefinitionRegistry

BeanDefinition解析完成后,最终要转化为bean对象,这中间就有BeanDefinition注册、解析生成bean对象的过程,而管理BeanDefinition的核心就是BeanDefinitionRegistry。

Interface for registries that hold bean definitions, for example RootBeanDefinition and ChildBeanDefinition instances. Typically implemented by BeanFactories that internally work with the AbstractBeanDefinition hierarchy.

第一段:BeanDefinitionRegistry是保存Bean定义的注册中心,例如保存RootBeanDefinition和ChildBeanDefinition实例。通常由BeanFactories实现,它在内部使用AbstractBeanDefinition层次结构。

This is the only interface in Spring’s bean factory packages that encapsulates registration of bean definitions. The standard BeanFactory interfaces only cover access to a fully configured factory instance.

第二段:这是SpringFramework的 Bean工厂包中封装Bean定义注册的唯一接口。标准的BeanFactory接口只涵盖对完全配置的工厂实例的访问。

Spring’s bean definition readers expect to work on an implementation of this interface. Known implementors within the Spring core are DefaultListableBeanFactory and GenericApplicationContext.

第三段:SpringFramework的Bean定义读取器在这个接口的实现上工作。SpringFramework核心中已知的实现类有DefaultListableBeanFactory和GenericApplicationContext。

从javadoc可知,BeanDefinitionRegistry是维护BeanDefinition的注册中心,它内部存放了IOC容器中Bean的定义信息,同时也是支撑其他组件和动态注册Bean的重要组件。

从源码来看,BeanDefinitionRegistry本质是一个存放BeanDefinition的容器:

// 源自实现类DefaultListableBeanFactory
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

这里需要注意,DefaultListableBeanFactory不仅是BeanDefinitionRegistry的主要实现,也是BeanFactory的主要实现,因此DefaultListableBeanFactory不仅是bean对象的统一管理容器,而且是BeanDefinition的统一管理容器。

其次,BeanDefinitionRegistry包含BeanDefinition的注册功能,即增、删、查:

// 注册
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
// 移除
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 获取
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

最后,在 SpringBoot源码解读与原理分析(二)组件装配 中使用ImportBeanDefinitionRegistrar以编程式向IOC容器中注册bean对象时,其核心方法registerBeanDefinitions的入参就有一个BeanDefinitionRegistry,自定义的组件的定义信息将注册到BeanDefinitionRegistry中。

public class WaiterRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("waiter222", new RootBeanDefinition(Waiter.class));
    }
}

3.5.6 设计BeanDefinition的意义

为了统一管理bean对象。

在面向对象的java开发中,是先编写Class类,再new出对象。Class类就相当于定义信息,有即定的规则(如有构造方法、getter、setter等)。按照这些规则,就可以生成对象、设定属性等等。

设计BeanDefinition的意义就是对IOC容器中的Bean进行定义抽取,只有抽取成统一类型或格式的模型,才能在后续的bean对象管理时进行统一管理,或者对特定的Bean进行特殊化处理。

简单地说,就是有了定义信息,按照既定的规则,就可以任意解析和生成bean对象,也可以根据实际需求对解析和生成对象的过程进行任意扩展。

分类专栏:SpringBoot源码解读与原理分析

文章来源:https://blog.csdn.net/weixin_42739799/article/details/135603860
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。