Spring BeanDefinition

什么是BeanDefinition

在这里插入图片描述

  1. BeanDefination包含了我们对bean做的配置,比如XML<bean/>标签的形式进行配置;
  2. Spring将我们对bean的定义信息进行了抽象,抽象后的实体就是BeanDefinition,并且Spring会以此作为标准来对Bean进行创建;
  3. BeanDefinition包含以下元数据:
    • 一个全限定类名,通常来说,就是对应的bean的全限定类名;
    • bean的行为配置元素,这些元素展示了这个bean在容器中是如何工作的,包括scope
      lifecycle callbacks
    • 这个bean的依赖信息;
    • 一些其他配置,比如连接池对象的最大连接数等;

Java正常创建Bean对象和spring创建对象的区别

  • 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这个接口为从其他任意类中获取或设置元数据提供了一个通用的规范;将数据接口和操作方法进行分离,提供一个获取属性跟设置属性的方法。
void setAttribute(String name, @Nullable Object value);

Object getAttribute(String name);

Object removeAttribute(String name);

boolean hasAttribute(String name);

String[] attributeNames();
  • org.springframework.beans.BeanMetadataElement这个接口提供一个方法去获取配置源对象,其实就是源文件。
@Nullable
Object getSource();

可以理解为,当我们通过注解的方式定义了一个IndexService对象时,此时的IndexService对应的BeanDefinition通过getSource方法返回的就是IndexService.class这个文件对应的一个File对象。

2. AbstractBeanDefinition

在这里插入图片描述

  • org.springframework.core.AttributeAccessorSupportAttributeAccessor将数据结构跟操作方法分离,数据结构就在AttributeAccessorSupport中。
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);
}

AbstractBeanDefinition通过继承了BeanMetadataAttributeAccessor,可以对BeanDefinition中的属性进行操作,这里属性仅仅代表BeanDefinition中的一个map。

为什么需要AbstractBeanDefinition?

AbstractBeanDefinitionBeanDefinition的大部分方法做了实现,同时定义了一系列常用及默认字段,这是因为BeanDefinition接口过于顶层,如果我们依赖BeanDefinition这个接口直接去创建实现类的话过于麻烦,所以通过AbstractBeanDefinition做了一个下沉,并给很多属性赋了默认值:

// 默认情况不是懒加载的
private boolean lazyInit = false;
// 默认情况不采用自动注入
private int autowireMode = AUTOWIRE_NO;
// 默认情况作为自动注入的候选bean
private boolean autowireCandidate = true;
// 默认情况不作为优先使用的bean
private boolean primary = false;
........
3. AbstractBeanDefinition的三个子类

GenericBeanDefinition

  • 替代原来的ChildBeanDefinition,比起ChildBeanDefinition更为灵活,ChildBeanDefinition在实例化的时候必须要指定一个parentNameGenericBeanDefinition不需要,通过注解配置的bean以及我们的配置类的BeanDefinition类型都是GenericBeanDefinition

ChildBeanDefinition

已经被GenericBeanDefinition替代;

RootBeanDefination

  • Spring在启动时都会实例化几个初始化的BeanDefinition,这几个BeanDefinition的类型都是RootBeanDefinition
4.AnnotatedBeanDefinition

这个接口继承BeanDefinition接口:

AnnotationMetadata getMetadata();

@Nullable
MethodMetadata getFactoryMethodMetadata();

相较于BeanDefinition,仅仅多提供了两个方法,

  • getMetadata():主要用于获取注解元数据,从接口的命名上看出,这个类主要用于保存通过注解方式定义的bean所对应的BeanDefinition,所以它多提供了一个关于获取注解信息的方法。

  • getFactoryMethodMetadata():这个方法跟我们的@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
6. BeanDefinition的具体使用的子类

在这里插入图片描述

什么是BeanDefinition的合并

一个BeanDefinition包含了很多的配置信息,包括构造参数,setter方法的参数还有容器特定的一些配置信息,比如初始化方法,静态工厂方法等,一个子BeanDefinition可以从它的父BeanDefinition继承配置信息,不仅如此,还可以覆盖其中的一些值或添加一些自己需要的属性,使用BeanDefinition的父子定义可以减少很多的重复属性设置,父BeanDefinition可以作为定义BeanDefinition的模板。

BeanDefinition会从父BeanDefinition中继承自身没有的属性,这个过程中,子BeanDefinition中已经存在的属性不会被父BeanDefinition中所覆盖。

注意

  • BeanDefinition中的class属性如果为null,同时父BeanDefinition又指定了class属性,那么子BeanDefinition也会继承这个class属性;

  • BeanDefinition必须要兼容父BeanDefinition中的所有属性;

  • 关于BeanDefinitionabstract属性的说明:

    1. 并不是作为父BeanDefinition就一定要设置abstract属性为true,abstract只代表这个BeanDefinition是否要被spring实例化并创建对应的Bean,如果为true,代表容器不需要去对其进行实例化;

    2. 如果一个BeanDefinition被当做父BeanDefinition使用,并且没有指定其class,那么必须指定其abstract为true。

    3. abstact=true一般会跟父BeanDefinition一起使用,因为当我们设置某个BeanDefinitionabstract=true时,一般都是要将其当做BeanDefinition的模板使用,否则这个BeanDefinition也没有意义,除非我们使用其他BeanDefinition来继承它的属性。

spring在哪些阶段做了合并

1. 扫描并获取到BeanDefinition

主要发生在invokeBeanFactoryPostProcessors阶段;
在这里插入图片描述

	public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory,
													   List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
        // .....
	    // 省略部分代码,省略的代码主要时用来执行程序员手动调用API注册的容器的后置处理器
        // .....

		// 发生一次bd的合并
        // 这里只会获取实现了BeanDefinitionRegistryPostProcessor接口的Bean的名字
			String[] postProcessorNames =             beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
                // 筛选实现了PriorityOrdered接口的后置处理器
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					// 去重
					processedBeans.add(ppName);
				}
			}
			// .....
            // 只存在一个internalConfigurationAnnotationProcessor 处理器,用于扫描
        	// 这里只会执行了实现了PriorityOrdered跟BeanDefinitionRegistryPostProcessor的后置处理器
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			// .....
        	// 这里又进行了一个bd的合并
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
                // 筛选实现了Ordered接口的后置处理器
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			// .....
        	// 执行的是实现了BeanDefinitionRegistryPostProcessor接口跟Ordered接口的后置处理器
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			boolean reiterate = true;
			while (reiterate) {
				reiterate = false;
            // 这里再次进行了一次bd的合并
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				for (String ppName : postProcessorNames) {
					if (!processedBeans.contains(ppName)) {
                        // 筛选只实现了BeanDefinitionRegistryPostProcessor的后置处理器
						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
						processedBeans.add(ppName);
						reiterate = true;
					}
				}
				sortPostProcessors(currentRegistryProcessors, beanFactory);
				registryProcessors.addAll(currentRegistryProcessors);
                // 执行的是普通的后置处理器,即没有实现任何排序接口(PriorityOrdered或Ordered)
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
				currentRegistryProcessors.clear();
			}
        // .....
        // 省略部分代码,这部分代码跟BeanfactoryPostProcessor接口相关,这节bd的合并无关,下节容器的扩展点中我会介绍
        // .....
		
	}

在这里插入图片描述

每次调用beanFactory.getBeanNamesForType都进行一次BeanDefinition合并,getBeanNamesForType这个方法主要目的是为了指定类型的BeanDefinition的名称,之后通过BeanDefinition的名称去找指定的BeanDefinition,然后获取对应Bean。

2. 实例化

spring在实例化一个对象时也会进行BeanDefinition合并。

第一次:
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
    // .....
	// 省略跟合并无关的代码
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // .....

第二次:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    // .....
    // 省略跟合并无关的代码
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    checkMergedBeanDefinition(mbd, beanName, args);

    // Guarantee initialization of beans that the current bean depends on.
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
        // ....
    }
    if (mbd.isSingleton()) {
        // ....
    }
    // ....
为什么要合并

BeanDefinition从父定义继承配置数据。子定义可以覆盖某些值或根据需要添加其他值。使用父BeanDefinition和子BeanDefinition定义可以节省很多输入。实际上,这是一种模板形式。父BeanDefinition不能单独实例化,因为它不完整,并且还被明确标记为抽象。当定义是抽象的时,它只能用作纯模板BeanDefinition,用作子定义的父定义。

在扫描阶段,之所以发生合并,是因为spring需要拿到指定了实现了BeanDefinitionRegistryPostProcessor接口的BeanDefinition的名称,也就是说spring需要用到BeanDefinition中的一系列属性做判断,所以进行了一次合并,spring需要用到BeanDefinition的属性,要保证获取到的BeanDefinition的属性是正确的。

为什么获取到的BeanDefinition中属性可能不正确?

  1. 作为子BeanDefinition,属性本身就有可能缺失;
  2. spring提供很多扩展点,在容器启动的时候,可能会修改BeanDefinition中的属性,

总结

之所以在每次需要用到BeanDefinition时都进行一次合并,是为了每次都拿到最新的,最有效的BeanDefinition,因为容器提供了一些扩展点我们可以修改BeanDefinition的属性。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值