spring5源码分析-BeanDefinition

一、引言

先来看一个案例,来引出咱们今天的主题。

spring.xml 文件配置了两个bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   https://www.springframework.org/schema/beans/spring-beans.xsd"
>

	<bean id="user" class="com.kk.entity.User" scope="prototype"/>

	<bean id="userService" class="com.kk.service.UserService" parent="user">
	</bean>

</beans>

测试类 

	public static void main(String[] args) {

		// xml配置,用这个容器启动
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

		System.out.println(applicationContext.getBean("userService"));
		System.out.println(applicationContext.getBean("userService"));
		System.out.println(applicationContext.getBean("userService"));
	}

打印结果

com.kk.service.UserService@62e136d3
       com.kk.service.UserService@c8e4bb0
       com.kk.service.UserService@6279cee3

需要思考问题:

1、通过结果我们可以很明显的看出,userService三个对象都是不同的,原因是因为在spring.xml中配置的userService,并且parent="user"。 而其中user的配置scope=prototype,所以导致userService也继承了该属性。

那么第一个需要思考的问题就是,在spring中是如何实现这个功能的?

2、当我们在xml中配置了一个bean,这些bean的信息在spring容器中是如何存储的?

二、什么是BeanDefinition

问题1:bean的信息在spring中是如何存储的?

org.springframework.beans.factory.config包下有一个BeanDefinition接口,在spring中,我们一般都简称bd。

那这个是做什么用的呢? 它里面就会有一些方法,是专门用来保存和获取我们所配置的bean信息。

比如我们看其中的一些方法:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    
    /**
	 * parentName表示父BeanDefinition的名字
	 */
	void setParentName(@Nullable String parentName);

	/**
	 * 返回父BeanDefinition的名字
	 */
	@Nullable
	String getParentName();

    /**
	 * 设置beanClass名字
	 * @param beanClassName
	 */
	void setBeanClassName(@Nullable String beanClassName);

	/**
	 * 获取设置beanClass名字
	 * @return
	 */
	@Nullable
	String getBeanClassName();

	/**
	 * 设置bean的scope,是单例还是原型
	 * @param scope
	 */
	void setScope(@Nullable String scope);

	/**
	 * 获取scope
	 * @return
	 */
	@Nullable
	String getScope();

	/**
	 * 设置是否懒加载
	 * @param lazyInit
	 */
	void setLazyInit(boolean lazyInit);
}

还有很多方法,小编就不一一展示了,小伙伴们可以去源码自行查看。

那么这些方法被谁实现了呢?

最开始,看到了org.springframework.beans.factory.support包中AbstractBeanDefinition提供了一些基础的实现逻辑,再这个基础之上然后又派生出两个不同的实现类。

GenericBeanDefinition 和 RootBeanDefinition有什么区别?

GenericBeanDefinition:在spring启动之后,会通过扫描的方式来进行获取程序员所定义的类信息,然后原封不动的保存到这个类型的BeanDefinition当中。

RootBeanDefinition:在有了原始信息的BeanDefinition之后,可能会由于某些bean会继承其他的bean信息,就会有一个合并的操作,合并之后的BeanDefinition就是RootBeanDefinition。

多说不练假把式,以userService为例,我们来看看在spring扫描之后,得到的BeanDefinition是什么样子的。

这个beanDefinitionMap是在DefaultListableBeanFactory中的一个属性,BeanFactory是spring的核心,其中就包括单例池等等一些重要属性。

通过图中所示,userService类型是GenericBeanDefinition的,并且scope为空,和我们在xml中配置一致。

三、BeanDefinition的合并操作

问题2:UserServcie是如何实现创建多个对象的?

在spring中,BeanDefinition合并之后的类型就变成了RootBeanDefinition,在DefaultListableBeanFactory中有个属性是mergedBeanDefinitions,专门用来保存合并之后的BeanDefinition。

在spring创建bean对象的时候,是根据合并后的BeanDefinition来进行创建的,在这里UserServcie的父类是User,User的scope是prototype,所以合并之后的UserServcie的BeanDefinition的scope也就是prototype。
所以在创建的时候,当然就不会是单例模式,最终就会创建多个。

合并的源码分析:

源码路径:org.springframework.beans.factory.support.AbstractBeanFactory中getMergedBeanDefinition()方法,主要逻辑都在这个里面。(小编也是一步一步找到这里的,步骤有点多,小编就直接跳到核心了)

// beanName = userService
// bd 原始的BeanDefinition 也就是GenericBeanDefinition这个类型的
// 第三个参数传入的null
protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {

		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;

			// Check with full lock now in order to enforce the same merged instance.
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}

			if (mbd == null || mbd.stale) {
				previous = mbd;
				// 如果bd的父bd为空
				if (bd.getParentName() == null) {
					// 如果ParentName就说明当前的bd是没有父类的
					// 并且如果类型是RootBeanDefinition,直接clone
					if (bd instanceof RootBeanDefinition) {
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
						// 否则把当前bd属性(自己本身)作为基础,创建一个RootBeanDefinition的bd
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// 否则,就说明当前bd它有父类
					BeanDefinition pbd;
					try {
						// 先获取父bd的beanName
						String parentBeanName = transformedBeanName(bd.getParentName());
						if (!beanName.equals(parentBeanName)) {
							// 递归调用,获取最终的父bd
							// 这里递归调用的原因是因为,父bd自己本身也有父bd,所以要递归调用,一只获取到最顶层的bd
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
										"': cannot be resolved without an AbstractBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}
					// pbd 是找出最顶级的父bd
					// 根据最顶级的父bd来创建一个RootBeanDefinition
					mbd = new RootBeanDefinition(pbd);
					// db 是传入的最原始的bd,也就是GenericBeanDefinition类型的
					// 把bd的属性设置给mbd, 而mbd是基于pbd来的,pbd是parent,bd是当前bd,把当前bd的属性设置给pbd就是合并(合并属性)
					mbd.overrideFrom(bd);
				}

				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}

				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}

				// 合并完成后就放入到mergedBeanDefinitions集合当中
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			return mbd;
		}
	}

四、最后总结

问题1:bean的配置信息,在spring中如何存储的?

是由BeanDefinition存储的,bd又分为几种不同的类型。第一种GenericBeanDefinition 存储最原始的bean配置信息 ,第二种 RootBeanDefinition 合并之后的类型,如果当前bd有父bd,就会合并成rootbd类型,如果没有父bd,也会把自己本身封装一个root类型的bd。

问题2: 为什么userService会基础user的scope属性?

原因就是因为bd的合并操作,以父bd的配置信息为基础创建了root类型的bd,然后再把自己本身的属性覆盖父bd的属性。

 

 

没看懂的小伙伴,你细品、细品、细细品。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页
实付 29.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值