【Spring源码阅读】BeanDefinition原理与加载流程

本文探讨Spring框架中的BeanDefinition接口及其在容器初始化中的作用。BeanDefinition存储了Bean的类信息和属性配置,通过AttributeAccessor接口实现属性注入。文章详细介绍了BeanDefinition的实现类,如AbstractBeanDefinition、RootBeanDefinition、ChildBeanDefinition和GenericBeanDefinition,并讲解了Spring解析加载BeanDefinition的过程,包括资源的前期准备、doLoadBeanDefinitions的校验和解析以及registerBeanDefinitions的创建步骤。
摘要由CSDN通过智能技术生成

BeanDefinition接口定义及其相关子类实现

在Spring容器初始化过程中,Spring会将配置文件中配置的Java类封装成一个个BeanDefinition。
BeanDefinition存储了具体代表Bean的类,并通过实现了AttributeAccessor接口定义了读写属性配置的相关方法。在基于xml配置Spring容器中,我们为某个Bean配置了具体的属性值,这些都根据name-value对映射到BeanDefinition的元数据集合attributes中,在实例化成具体Bean的时候通过AttributeAccessor接口实现的方法完成属性注入。
在这个基础上BeanDefinition定义一个Bean基于Spring容器中的相关属性能力,比如是否为单例,是否支持懒加载,是否支持自动装配、为自动装配首选等。
BeanDefinition作为接口定义,有众多的实现类,常用的如下图所示:
image

  1. AbstractBeanDefinition:BeanDefinition的较为完整、具体的抽象实现类,定义了三大子类ChildBeanDefinition,RootBeanDefinition,GenericBeanDefinition的基础公用实现,具体实现了BeanDefinition属性付覆写,解析获取BeanDefinition对应的BeanClass和Spring相关属性能力的读写等(比如是否为单例,支持自动装配等)
  2. RootBeanDefinition/ChildBeanDefinition:BeanDefinition存在父子关系,子BeanDefinition用ChildBeanDefinition表示,父BeanDefinition或没有父子关系的用RootBeanDefinition表示。
  3. GenericBeanDefinition: 相比于RootBeanDefinition和ChildBeanDefinition在定义的时候就必须硬编码,GenericBeanDefinition的优点可以动态的为GenericBeanDefinition设置parent。
  4. AnnotatedBeanDefinition:读取基于注解定义的Bean。

Spring解析加载BeanDefinition流程

最终加载资源前的复杂过程

在最终开始加载资源,会经过一系列繁琐的准备工作,核心加载逻辑在下一节doLoadBeanDefinitions加载逻辑实现描述,下面先是前期的准备工作:
在Spring初始化容器过程,第一步首先创建BeanFactory的时候,会调用AbstractApplicationContext的obtainFreshBeanFactory方法,里面会调用AbstractApplicationContext类中实现的refreshBeanFactory方法,在这个方法里,调用了loadBeanDefinitions(beanFactory)来解析BeanDefinition,具体如下所示

protected final void refreshBeanFactory() throws BeansException {
   
	if (hasBeanFactory()) {
   
		destroyBeans();
		closeBeanFactory();
	}
	try {
   
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);
		// 加载Beanfinitions
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
   
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
   
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

具体实现如下:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   
	// 先创建一个基于xml的BeanDefinitionReader。
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// 设置上下文环境和资源加载器,环境主要根据profile进行配置
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	//设置xml解析工具
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// 在初始化里调用了reader.setValidating(this.validating),其中this.validating默认为true,表示需要验证xml文件格式
	initBeanDefinitionReader(beanDefinitionReader);
	// 尝试获取配置文件位置后,调用XmlBeanDefinitionReader内方法进行加载
	loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
   
		reader.loadBeanDefinitions(configResources);
	}
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
   
		reader.loadBeanDefinitions(configLocations);
	}
}

XmlBeanDefinitionReader的loadBeanDefinitions方法定义在抽象父类AbstractBeanDefinitionReader中:

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
   
    // 先调用这个方法    
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
   
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		//根据传入的位置数组遍历加载,对加载的BeanDefinition个数进行累加返回
		for (String location : locations) 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值