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

1,718 阅读7分钟

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图

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图就变成了下面
这样了

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;
	}
}