专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Spring源码分析-一、BeanDefinition体系结构分析

BeanDefinition体系分析

引入

在Java中, 我们通过一个Class对象来描述一个类的信息, 比如方法、属性等信息, 在Spring中, 则是通过
BeanDefinition来描述一个Bean对象的, 比如这个对象的作用域(单例/多例), 本文将会从BeanDefinition的
顶层接口开始讲起, 指明每一个接口/类的作用, 从而能够从整体上看懂BeanDefinition的体系结构

BeanMetadataElement

public interface BeanMetadataElement {
    Object getSource();
}

分析: 该接口仅仅定义了一个getSource方法, 该方法用于返回一个source源, 其实就是返回一个Class文件在
      磁盘中的绝对路径而已, 在Spring中, BeanDefinition是间接的实现了这个接口的, 从而返回的是在
      BeanDefinition中定义的类的绝对路径

AttributeAccessor

public interface AttributeAccessor {
    void setAttribute(String name, @Nullable Object value);

    Object getAttribute(String name);

    Object removeAttribute(String name);

    boolean hasAttribute(String name);

    String[] attributeNames();
}

分析: 由名字可以知道, 这就是一个属性的访问接口, 提供了对属性的访问、设置功能, 同样的, BeanDefiniton
也是间接的实现了这个接口的, 之所以要实现这个接口是为了存储一些属性, 这些属性不应该放入到Bean的定义
中, 即为了实现类的单一职责, BeanDefinition仅仅用来定义bean对象的公共属性, 对于特殊的bean对象, 如
果还需要其它属性的定义, 那么就应该通过AttributeAccessor接口提供的功能来实现

AttributeAccessorSupport

public abstract class AttributeAccessorSupport implements AttributeAccessor {
    /** Map with String keys and Object values. */
    private final Map<String, Object> attributes = new LinkedHashMap<>();

    public void setAttribute(String name, @Nullable Object value) {
        if (value != null) {
            this.attributes.put(name, value);
        }
        else {
            removeAttribute(name);
        }
    }

    public Object getAttribute(String name) {
        return this.attributes.get(name);
    }

    public Object removeAttribute(String name) {
        return this.attributes.remove(name);
    }

    public boolean hasAttribute(String name) {
        return this.attributes.containsKey(name);
    }

    public String[] attributeNames() {
        return StringUtils.toStringArray(this.attributes.keySet());
    }
}

分析: 由上面的定义可以看到, AttributeAccessorSupport实现了AttributeAccessor接口, 并对其方法进行
了实现, 可以清晰的看到, 其就是通过一个map来存储我们所说的额外的属性的

那么如何对额外属性以及源数据进行整合的呢?即如何对BeanMetadataElement和AttributeAccessor整合呢

BeanMetadataAttributeAccessor实现额外属性和源数据的整合

public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {
    private Object source;

    public void setSource(@Nullable Object source) {
        this.source = source;
    }

    public Object getSource() {
        return this.source;
    }

  .....
}

分析: BeanMetadataAttributeAccessor类通过继承AttributeAccessorSupport以及实现BeanMetadataElement
接口来对两个功能进行了整合, 这里用Object来描述一个Bean对象Class文件的绝对路径, 大家有兴趣可以在看
完整篇文章后调用该API看看效果

总结一

BeanMetadataAttributeAccessor继承AttributeAccessorSupport, 从而拥有了额外属性的功能, 即可以通
过调用父类的getAttribute和setAttribute来操作AttributeAccessorSupport中的map, 通过实现
BeanMetadataElement接口, 实现该接口的getSource方法来实现了可以存储class对象的源文件地址的功能

BeanDefinition接口解析

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
        // 两个常量, 方便在定义BeanDefinition的时候直接引用
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

        // 设置/获取父BeanDefinition, 下面会专门抽出来讲解这个
    void setParentName(@Nullable String parentName);
    String getParentName();

        // 设置Bean的类名, 之后在创建bean对象的时候就是创建这里定义的类名
    void setBeanClassName(@Nullable String beanClassName);
    String getBeanClassName();

        // 设置作用域, 单例/原型
    void setScope(@Nullable String scope);
    String getScope();

        // 设置是否延迟加载
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();

        // 设置是否依赖其它bean对象, 如果有依赖其它bean对象, 那么只有当其它bean对象被创建后才会创建当前
        // beanDefinition中定义的对象
    void setDependsOn(@Nullable String... dependsOn);
    String[] getDependsOn();

        // 设置是否允许当前bean对象被其它对象注入, 其是否允许其它对象通过@Autowired类似的方式注入
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isAutowireCandidate();

        // 当出现一个类对象有多个实例在容器中时, @Autowired注入的时候是否以当前对象为主, 如果没设置
        // 则会报错, 错误信息就是发现在容器中有多个xxx类实例
    void setPrimary(boolean primary);
    boolean isPrimary();

    void setFactoryBeanName(@Nullable String factoryBeanName);
    String getFactoryBeanName();

        // 在xml配置的时候, 有如下配置<bean id="bean1" factory-method="test" class="com.A">,
        // 那么在容器中就会有一个BeanDefinition, id为bean1, FactoryMethodName为test, 由该BeanDefinition
        // 生成的bean对象是由com.A的test方法生成的
    void setFactoryMethodName(@Nullable String factoryMethodName);
    String getFactoryMethodName();

        // 等价于xml配置中bean标签的子标签constructor-args, 用来定义创建bean对象时的构造器, 在创建对象
        // 的时候, 会根据ConstructorArgumentValues的值来选择构造器,
    ConstructorArgumentValues getConstructorArgumentValues();
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

        // 等价于xml配置中bean标签中的子标签property, 用来在创建bean对象的时候填充对象中的属性
    MutablePropertyValues getPropertyValues();
    void setInitMethodName(@Nullable String initMethodName);

        // 设置初始化方法
    String getInitMethodName();

        // 该beanDefinition中定义的类是否是抽象类, 如果该值为true, 则不会创建bean对象
    boolean isAbstract();

  ......
}

分析: BeanDefinition定义了一个通用的bean对象的大部分方法, 以及一些常量, 这些方法都会在其子类
AbstractBeanDefinition中实现, 同样的, 一些bean对象可能由额外的属性, 这些操作都在
BeanMetadataAttributeAccessor这个类中实现了, 接下来就是通过AbstractBeanDefinition来实现接口
BeanDefinition, 以及继承BeanMetadataAttributeAccessor类, 这样就能够将这些功能统一起来了, 下面
我们先来看看目前的一个类的UML图

64_1.png

AbstractBeanDefinition及其直接子类分析

AbstractBeanDefinition的主要功能就是实现BeanDefinition中的方法, 并在方法要求的常量上定义应该拥有
的属性, 比如scope属性, 在此基础上, 通过继承BeanMetadataAttributeAccessor类, 整合了上面我们所说
的额外属性以及获取源文件这样的功能

AbstractBeanDefinition仅仅只有三个直接的子类, 分别是:
RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition, 下面我们对这三个子类进行相关说明

在Spring2.5之前, 仅仅只有RootBeanDefinition、ChildBeanDefinition两个子类, 我们一般通过
RootBeanDefinition来定义一个Bean对象, 但是有时候会出现这么一种情况, 我们需要连续定义多个
beanDefinition, 即连续创建多个RootBeanDefinition, 这时可能会出现很多相同的定义, 如下:

RootBeanDefinition bd1 = new RootBeanDefinition();
bd1.setBeanClassName( "com.test1" );
bd1.setScope( "singleton" );
....

RootBeanDefinition bd2 = new RootBeanDefinition();
bd2.setBeanClassName( "com.test2" );
bd2.setScope( "singleton" );
....

RootBeanDefinition bd3 = new RootBeanDefinition();
bd3.setBeanClassName( "com.test3" );
bd3.setScope( "singleton" );
....

由上述定义可以看到, 假设我们需要同时定义很多个BeanDefinition, 可能会出现这些定义都会有相同的设置,
从而造成冗余, 于是Spring就开发出了一种伪继承模式, 通过parent属性来实现父子定义, 子类可以拥有父类
的定义从而不用再重复定义相同的属性了, 这就是ChildBeanDefinition和RootBeanDefinition的由来, 由此
可见, 我们可以将多个bean的公共部分放置在一个RootBeanDefinition中, 然后创建多个ChildBeandefinition,
并且指定parent为RootBeanDefinition即可, 如下:

RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setScope( BeanDefinition.SCOPE_SINGLETON );
rootBeanDefinition.setLazyInit( false );
rootBeanDefinition.setAbstract( false ); // 设置为抽象的, 则在创建bean对象的时候不会被创建
context.registerBeanDefinition( "root", rootBeanDefinition );

ChildBeanDefinition childBeanDefinition1 = new ChildBeanDefinition( "root" );
childBeanDefinition1.setBeanClass( TestClass1.class );
context.registerBeanDefinition( "child1", childBeanDefinition1 );

ChildBeanDefinition childBeanDefinition2 = new ChildBeanDefinition( "root" );
childBeanDefinition2.setBeanClass( TestClass2.class );
context.registerBeanDefinition( "child2", childBeanDefinition2 );

总结: 通过上述的方式, 就可以将一些公共的设置给抽离出来了, 从而使得RootBeanDefinition既可以独立成
一个bean对象的定义, 也可以作为多个bean对象的父类, 而ChildBeanDefinition则可以指定parent为root来
共享公共的定义

在Spring2.5以后, Spring又定义了一个GenericBeanDefinition, 从而打破了原来RootBeanDefinition以及
ChildBeanDefinition的弊端, ChildBeanDefinition在创建时必须指定一个parent, 而RootBeanDefinition
的setParent方法是无效的(可以通过源码看到), 而新的GenericBeanDefinition却不存在这样的情况, 所以
GenericBeanDefinition也称为通用的BeanDefinition, 此时我们的BeanDefinition的UML图就变成了下面
这样了

64_2.png

GenericBeanDefinition的子类

GenericBeanDefinition作为通用的BeanDefinition, 打破了原来RootBeanDefinition和ChildBeanDefinition
的弊端, 成为了一个通用的BeanDefinition, 但是我们在使用Spring的时候可以知道, 在一个Bean对象是可以
定义一些注解的, 比如我们可以自定义一个@Test注解, 那么一个GenericBeanDefinition是没法存储下这些注
解的信息的, 于是为了能够存储注解信息, GenericBeanDefinition又衍生出了两个子类, 分别是:
AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition, 这两个子类的定义几乎是类似的,
只不过AnnotatedGenericBeanDefinition比ScannedGenericBeanDefinition多了一些定义而已, 如下:

public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
    private final AnnotationMetadata metadata;

    public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
        this.metadata = metadataReader.getAnnotationMetadata();
        setBeanClassName(this.metadata.getClassName());
    }

    @Override
    public final AnnotationMetadata getMetadata() {
        return this.metadata;
    }

    @Override
    public MethodMetadata getFactoryMethodMetadata() {
        return null;
    }
}

文章永久链接:https://tech.souyunku.com/36529

未经允许不得转载:搜云库技术团队 » Spring源码分析-一、BeanDefinition体系结构分析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们