Spring框架之BeanDefinition源码分析


前言

在分析Spring IOC中bean的创建执行流程的时候,遇到了这么一个对象BeanDefinition,其实呢这个也挺重要的,bean工厂创建对象就是根据这个来的,这里面记录了装配到容器中所有的bean对象信息,由于篇幅过长就单独提出来写了一篇文章来介绍。

一、BeanDefinition

继续顺着前面的思路走,前面说到在刷新获取BeanFactory的时候做了这么一件事:加载BeanDefinition

loadBeanDefinitions(beanFactory);

我们找到BeanDefinition,由于它是个接口我们看它默认的实现子类AbstractBeanDefinition。咱看一下这个类里面的一些属性,其实就可以看出来,这个装载了很多我们初始化bean配置的信息,例如单例、懒加载等。

	...
	private volatile Object beanClass;
	private String scope = SCOPE_DEFAULT; //单例,默认为单例
	private boolean abstractFlag = false;
	private Boolean lazyInit; //是否懒加载
	private int autowireMode = AUTOWIRE_NO; //自动装配
	private int dependencyCheck = DEPENDENCY_CHECK_NONE; //默认不检查依赖
	private String[] dependsOn; //依赖的bean列表
	private boolean autowireCandidate = true;
	private boolean primary = false;
	private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
	private Supplier<?> instanceSupplier;
	private boolean nonPublicAccessAllowed = true;
	private boolean lenientConstructorResolution = true;
	private String factoryBeanName;  //创建bean的bean工厂名称
	private String factoryMethodName; //创建bean的bean工厂方法
	private ConstructorArgumentValues constructorArgumentValues; //有参构造的参数
	private MutablePropertyValues propertyValues;	//bean的属性及对应名称
	private MethodOverrides methodOverrides = new MethodOverrides(); //被覆盖的方法
	private String initMethodName;  //初始化和销毁方法
	private String destroyMethodName;
	private boolean enforceInitMethod = true;   //是否需要执行初始化和销毁方法
	private boolean enforceDestroyMethod = true;
	private boolean synthetic = false;
	private int role = BeanDefinition.ROLE_APPLICATION;
	private String description;	//一些描述信息等
	private Resource resource; //定义bean的资源
	...

看了一下其实这个类没有什么复杂的内容,只是对我们配置的beans.xml进行了解析,然后把相关配置写到了属性中,可以在DefaultListableBeanFactory注册BeanDefinition的方法(registerBeanDefinition)中打个断点看一下。
在这里插入图片描述
这个类简单的理解可以当做Bean对象的Entity,我们找一些用到这个类的类,方法其实很简单,用这个类作为开头全局搜一下相关的类分析一下可以找到许多,找几个看一下。

二、BeanDefinitionBuilder

看名称就知道这是BeanDefinition的构建器,具体干啥的呢看一下内部的方法。可以看到其内部的许多方法都与上面的BeanDefinition中的属性相配合的,可以用其手动的构建一个BeanDefinition对象。
来手动试一下:

public static void main(String[] args) {
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class) ;
		beanDefinitionBuilder
				.addPropertyValue("name","张三")
				.addPropertyValue("age",18)
				.setScope("singleton");
		BeanDefinition beanDefinition= beanDefinitionBuilder.getBeanDefinition() ;
		System.out.println(beanDefinition);//这一行是用来打断点的
	}

可以看到可以使用其手动的构建一个BeanDefinition对象
在这里插入图片描述

三、BeanDefinitionHolder

BeanDefinition的持有者,其内部有两个属性是beanName也就是XML中配置的BeanId,还有一个是BeanDefinition,方法上没有什么特殊的方法。

四、BeanDefinitionParserDelegate

其实本来想看BeanDefinitionDocumentReader类的,但是其内部涉及到了这个类就先看一下它了,名称直译意思是BeanDefinition转换的委托类,点进去后看到了啥,一堆和Bean的XML配置相关的信息,也就是说它是Bean对象由XML转换为BeanDefinition的委托类。在这里插入图片描述
再里面巴拉巴拉相关的方法,看了其中的方法名就知道核心方法是把XML转换为BeanDefinition对象的方法

//大家应该都知道Element是XML解析完的对象,BeanName是XML中配置的beanID
public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, @Nullable BeanDefinition containingBean) {
		...
		try {
			//新创建一个BeanDefinition
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
			//下面是解析相关的内容放到BeanDefinition对应的属性中,具体干啥就不细看了
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			parseConstructorArgElements(ele, bd);
			parsePropertyElements(ele, bd);
			parseQualifierElements(ele, bd);
			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));
			return bd;
		}
		...
	}

还有一个重写的方法最终解析为BeanDefinitionHolder,BeanDefinition的持有者对象。

五、BeanDefinitionDocumentReader

同样看名称就明白这是从Document文档例如XML中读取配置BeanDefinition的,直接看一下其核心方法,由于它是个接口,我们看它默认的实现子类DefaultBeanDefinitionDocumentReader中的方法,看其参数是Element类型的,这个大家应该熟悉是XML的解析类型,也就是说到了这里XML已经被解析成了Element对象了,还有一个参数是BeanDefinitionParserDelegate类型的,来解析Element变成BeanDefinition的

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		...
	}
	//根据断点最终走到了这个方法,最后解析为了持有者对象
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		...
		// 发送注册事件
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

BeanComponentDefinition
本来搜不到它的,但是打断点分析的过程用到了它,看一下相关的源代码信息,一个BeanDefinition的数组和一个BeanReference的数组,怎么说呢,这个类继承了BeanDefinitionHolder,但是其内部不是单个的了,而是放了多个相关的BeanDefinition信息。

getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

这一行其实是监听器,有没有发现一个问题,当走到这里方法的返回参数为void空的,但是Bean对象的相关配置从XML中解析出来了,发现这里是空的,后面没有了,往回找,肯定是哪个方法漏掉了细节,最终找到了

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

这里最终指向了BeanDefinitionRegistry类,BeanDefinitionRegistry管理类,来看一下。

六、BeanDefinitionRegistry

其实只简单的看了一下BeanDefinition这个类的一些属性可以判断这就是记录bean的一些配置信息,工厂用来实例化bean的时候就根据这些配置信息来获取。咱看一下使用BeanDefinition类的注册类BeanDefinitionRegistry,这是个接口,看其提供的方法都是一些BeanDefinition的管理方法。

public interface BeanDefinitionRegistry extends AliasRegistry {
	//注册
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;
	//移除
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	//获取
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	boolean containsBeanDefinition(String beanName);
	String[] getBeanDefinitionNames();
	int getBeanDefinitionCount();
	boolean isBeanNameInUse(String beanName);
}

具体的其实可以看其子类DefaultListableBeanFactory,里面有这么一个属性,这就是传说中的bean容器,以配置文件中的id为key存放了N多Bean的模板,其实看上面的注册移除管理就比较简单了,可以理解为Map的操作。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

到此,XML解析(当然这只是后面,前面XML解析为Document到Element就不写了,有空再说)为BeanDefinition后面放到容器里的顺序捋清了。

总结

该洗洗睡了,明天画个流程图和时序图补上。
按照上面的分析最终梳理出来这么个逻辑:

  1. 前面操作把Beans.xml解析成了Document Element对象,这些省略了。
  2. 调用BeanDefinitionDocumentReader中的parseBeanDefinitions()方法。
  3. parseBeanDefinitions又调用了BeanDefinitionParserDelegate中的parseBeanDefinitionElement()方法,将Element转换成了BeanDefinitionHolder。
  4. parseBeanDefinitionElement()中有一步操作,调用Utils将BeanDefinitionHolder中的BeanDefinition注册到DefaultBeanDefinitionDocumentReader内部的容器中。
  5. DefaultBeanDefinitionDocumentReader最终通过registerBeanDefinition将BeanDefinition放到了一个Map对象中,这就是对象管理的容器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值