跳至主要內容

5、spring中Beanefinition完全解析

安图新大约 6 分钟

5、spring中Beanefinition完全解析

大家用过 spring 的应该都知道 BeanDefinition 是记录 bean 的定义信息的,但是我们看源码时会发现,源码中通过 BeanDefinition 接口派生出来好多类,比如 GenericBeanDefinition,AnnotatedGenericBeanDefinition,RootBeanDefinition 等等,那么他们之间有什么区别,都是在什么情况下用到的呢?这篇文章就带大家一起探讨。

1,BeanDefinition 常用类关系图

![ ][nbsp]

2,BeanDefinition 接口

BeanDefinition 是一个接口,定义了修改和获取 Bean 属性值和元数据信息。在 DefaultListableBeanFactory 中定义了 BeanDefinitionMap 来记录解析的 BeanDefinition,定义了 beanDefinitionNames 来记录 BeanDefinition 名字列表:

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

3,AbstractBeanDefinition

		new AbstractBeanDefinition() {


            @Override
            public void setParentName(String parentName) {



            }

            @Override
            public String getParentName() {


                return null;
            }

            @Override
            public AbstractBeanDefinition cloneBeanDefinition() {


                return null;
            }
        };

AbstractBeanDefinition 是 BeanDefinition 接口的抽象实现,除了 BeanDefinition 中的以上三个方法没实现,其余的全部实现了。

4,GenericBeanDefinition

GenericBeanDefinition 类是 AbstractBeanDefinition 类的具体实现,义为通用的 BeanDefinition。

一般我们通过 xml 定义的普通 bean 在源码中会被解析为 GenericBeanDefinition。

如下 xml

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="a" class="com.mashibing.A"></bean>
</beans>

调试信息

![ ][nbsp 1]

5,RootBeanDefinition

RootBeanDefinition 也是 AbstractBeanDefinition 的其中一个实现类,它可以单独作为一个 BeanDefinition,也可以作为其他 BeanDefinition 的父类。RootBeanDefinition 在 AbstractBeanDefinition 的基础上定义了更多属性。

在 spring 源码的 refresh 方法中的 invokeBeanFactoryPostProcessors(beanFactory)去执行所有的 BeanFactoryPostProcessor 时会通过去遍历 beanDefinitionNames 集合,然后把 beanDefinitionMap 中的 BeanDefinition 全部合并为 RootBeanDefinition,并且缓存到 mergedBeanDefinitions 中,这样在实例化 bean 时,通过 getMergedLocalBeanDefinition 直接从 mergedBeanDefinitions 中取出来即可。

/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

![ ][nbsp 2]

![ ][nbsp 3]

protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {



		synchronized (this.mergedBeanDefinitions) {


			// 用于存储bd的MergedBeanDefinition
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;

			// Check with full lock now in order to enforce the same merged instance.
			// 检查beanName对应的MergedBeanDefinition是否存在于缓存中
			if (containingBd == null) {


				mbd = this.mergedBeanDefinitions.get(beanName);
			}

			// 如果缓存中没有
			if (mbd == null || mbd.stale) {


				previous = mbd;
				// 如果bd的parentName为空,代表bd没有父定义,无需与父定义进行合并操作
				if (bd.getParentName() == null) {


					// Use copy of given root bean definition.
					// 如果bd的类型为RootBeanDefinition,则bd的MergedBeanDefinition就是bd本身,则直接克隆一个副本
					if (bd instanceof RootBeanDefinition) {


						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {


						// 否则,将bd作为参数,构建一个RootBeanDefinition。
						// 正常使用下,BeanDefinition在被加载后是GenericBeanDefinition或ScannedGenericBeanDefinition
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {


					// Child bean definition: needs to be merged with parent.
					// bd存在父定义,需要与父定义合并
					BeanDefinition pbd;
					try {


						// 获取父bean的名称,并进行转换
						String parentBeanName = transformedBeanName(bd.getParentName());
						// 如果当前beanName和父beanName不相同,那么递归调用合并方法
						if (!beanName.equals(parentBeanName)) {


							pbd = getMergedBeanDefinition(parentBeanName);
						}
						// 如果父定义的beanName与bd的beanName相同,则拿到父BeanFactory,
						// 只有在存在父BeanFactory的情况下,才允许父定义beanName与自己相同,否则就是将自己设置为父定义
						else {


							BeanFactory parent = getParentBeanFactory();
							// 如果父BeanFactory是ConfigurableBeanFactory,则通过父BeanFactory获取父定义的MergedBeanDefinition
							if (parent instanceof ConfigurableBeanFactory) {


								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {


								// 如果父BeanFactory不是ConfigurableBeanFactory,则抛异常
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
										"': cannot be resolved without a ConfigurableBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {


						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve p标注 @Bean 注解的类会使用构造方法自动装配

arent bean definition '" + bd.getParentName() + "'", ex);
					}
					// Deep copy with overridden values.
					// 使用父定义pbd构建一个新的RootBeanDefinition对象
					mbd = new RootBeanDefinition(pbd);
					// 使用bd覆盖父定义
					mbd.overrideFrom(bd);
				}

				// Set default singleton scope, if not configured before.
				// 如果没有指定scope,那么设置默认的scope为单例
				if (!StringUtils.hasLength(mbd.getScope())) {


					mbd.setScope(SCOPE_SINGLETON);
				}

				// A bean contained in a non-singleton bean cannot be a singleton itself.
				// Let's correct this on the fly here, since this might be the result of
				// parent-child merging for the outer bean, in which case the original inner bean
				// definition will not have inherited the merged outer bean's singleton status.
				// 如果containingBd不为空 && containingBd不为singleton && mbd为singleton,则将mdb的scope设置为containingBd的scope
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {


					mbd.setScope(containingBd.getScope());
				}

				// Cache the merged bean definition for the time being
				// (it might still get re-merged later on in order to pick up metadata changes)
				// 将beanName与mbd放到mergedBeanDefinitions缓存,以便之后可以直接使用
				if (containingBd == null && isCacheBeanMetadata()) {


					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			if (previous != null) {


				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			// 返回MergedBeanDefinition
			return mbd;
		}
	}

![ ][nbsp 4]

6,ChildBeanDefinition

ChildBeanDefinition 继承自 AbstractBeanDefinition。其相当于一个子类,不可以单独存在,必须依赖一个父 BeanDetintion,构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。它可以从父类继承方法参数、属性值,并可以重写父类的方法,同时也可以增加新的属性或者方法。

从 Spring 2.5 开始,以编程方式注册 Bean 定义的首选方法是 GenericBeanDefinition,GenericBeanDefinition 可以有效替代 ChildBeanDefinition 的绝大分部使用场合。

7,AnnotatedBeanDefinition

AnnotatedBeanDefinition 是 BeanDefinition 子接口之一,该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据。一般情况下,通过注解方式得到的 Bean(@Component、@Bean),其 BeanDefinition 类型都是该接口的实现类。

public interface AnnotatedBeanDefinition extends BeanDefinition {



	// 获得当前 Bean 的注解元数据
	AnnotationMetadata getMetadata();

	// 获得当前 Bean 的工厂方法上的元数据
	MethodMetadata getFactoryMethodMetadata();
}

该接口可以返回两个元数据的类:

  • AnnotationMetadata:主要对 Bean 的注解信息进行操作,如:获取当前 Bean 标注的所有注解、判断是否包含指定注解。
  • MethodMetadata:方法的元数据类。提供获取方法名称、此方法所属类的全类名、是否是抽象方法、判断是否是静态方法、判断是否是 final 方法等。

8,ScannedGenericBeanDefinition

ScannedGenericBeanDefinition 继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service、@Controller 也同理。

其在源码中是在 loadBeanDefinition 时解析 xml 配置文件,如果包含 component-scan 标签,就扫描 component-scan 的 base-package 指定的包下包含@Component 注解(当然包括@Service,@Controller)的类,添加的 beanDefinitionMap 中注入进来。
![ ][nbsp 5]

9,AnnotatedGenericBeanDefinition

AnnotatedGenericBeanDefinition 继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述通过@Import 注解方式注入的 Bean。

10,ConfigurationClassBeanDefinition

ConfigurationClassBeanDefinition 继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述在标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean。
其功能特点如下:

  • 如果 @Bean 注解没有指定 Bean 的名字,默认会用方法的名字命名 Bean。
  • 标注 @Configuration 注解的类会成为一个工厂类,而标注 @Bean 注解的方法会成为工厂方法,通过工厂方法实例化 Bean,而不是直接通过构造方法初始化。
  • 标注 @Bean 注解的类会使用构造方法自动装配。 [nbsp]: https://cdn.hotmindshare.com/custom/images/2024/2/22/134/1708578247723.png [nbsp 1]: https://cdn.hotmindshare.com/custom/images/2024/2/22/134/1708578248053.png [nbsp 2]: https://cdn.hotmindshare.com/custom/images/2024/2/22/134/1708578248314.png [nbsp 3]: https://cdn.hotmindshare.com/custom/images/2024/2/22/134/1708578248693.png [nbsp 4]: https://cdn.hotmindshare.com/custom/images/2024/2/22/134/1708578249066.png [nbsp 5]: https://cdn.hotmindshare.com/custom/images/2024/2/22/134/1708578249475.png
上次编辑于:
贡献者: Andy