死磕Spring之IoC篇 - Bean 的“前身”

月圓吖發表於2021-02-23

該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀

Spring 版本:5.1.14.RELEASE

開始閱讀這一系列文章之前,建議先檢視《深入瞭解 Spring IoC(面試題)》這一篇文章

Bean 的“前身”

我們在 Spring 中通常以這兩種方式定義一個 Bean:面向資源(XML、Properties)面向註解。如今 Spring Boot 被廣泛應用,通過註解定義一個 Bean 的方式變得更為普遍,因為在實際的開發過程中註解的方式相比於 XML 檔案更加輕便,可以有效地提高工作效率。你是否這瞭解這兩種方式在 Spring 內是如何進行處理的,將我們的配置資訊轉換成 Spring Bean,並管理著這些它們的生命週期

Spring Bean 的生命週期 可以看到,BeanDefinition 可以說是 Bean 的“前身”,首先進入 Bean 的元資訊的配置、解析和註冊階段,然後才開始 Bean 的例項化和初始化等工作。接下來,我們就一起來看看 Bean 的“前身”是什麼

BeanDefinition 是 Spring Framework 中定義 Bean 的配置元資訊介面,主要包含一下資訊:

  • Bean 的類名
  • Bean 行為配置類,如作用域、自動繫結模式、生命週期回撥等
  • 其他 Bean 引用,又可稱作合作者或者依賴
  • 配置設定,比如 Bean 屬性

BeanDefinition 體系結構

org.springframework.beans.factory.config.BeanDefinition 介面的類圖如下所示:

死磕Spring之IoC篇 - Bean 的“前身”

總覽:


  • org.springframework.core.AttributeAccessor 介面,用於獲取後設資料,在實現類中通過 LinkedHashMap 集合儲存後設資料,例如通過 XML 的 <meta /> 標籤定義的一些元資訊會儲存在其中

  • org.springframework.beans.BeanMetadataElement 介面,用於獲取定義 Bean 的源物件,在實現類中通過 Object 物件儲存,所謂的源物件就是定義這個 Bean 的資源(XML 標籤物件或者 .class 檔案資源物件)


  • org.springframework.beans.factory.config.BeanDefinition 介面,定義一個 Bean 的元資訊

  • org.springframework.beans.factory.support.AbstractBeanDefinition 抽象類,實現 BeanDefinition 介面,包含了一個 Bean 幾乎所有的元資訊

  • org.springframework.beans.factory.support.GenericBeanDefinition,繼承 AbstractBeanDefinition 抽象類,多了一個 parentName,表示有繼承關係,是一個標準 Bean 元資訊物件,通過 XML 定義的 Bean 會解析成該物件

  • org.springframework.beans.factory.annotation.AnnotatedBeanDefinition 介面,繼承 BeanDefinition 介面,定義註解類的元資訊,例如通過 @Component 註解定義的 Bean,那麼註解類的元資訊會包含編譯後的 .class 檔案的所有資訊

  • org.springframework.context.annotation.ScannedGenericBeanDefinition,繼承 GenericBeanDefinition,實現 AnnotatedBeanDefinition 介面,多了一個 AnnotationMetadata 註解類元資訊物件,例如通過 @Component 註解定義的 Bean 會解析成該物件

  • org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition,繼承 GenericBeanDefinition,實現 AnnotatedBeanDefinition 介面,和 ScannedGenericBeanDefinition 類似,通過 @Import 匯入的 Configuration Class 會解析成該物件


  • org.springframework.beans.factory.support.RootBeanDefinition,繼承 AbstractBeanDefinition 抽象類,表示合併後的 BeanDefinition 物件。在 Spring BeanFactory 初始化 Bean 的前階段,會根據 BeanDefinition 生成一個 RootBeanDefinition(具有層次性則會進行合併),用於後續例項化和初始化

  • org.springframework.context.annotation.ConfigurationClassBeanDefinition$ConfigurationClassBeanDefinition 私有靜態類,繼承 RootBeanDefinition,實現了 AnnotatedBeanDefinition 介面,和 AnnotatedGenericBeanDefinition 類似,沒有繼承關係,通過 @Bean 定義的方法會解析成該物件


  • org.springframework.beans.factory.config.BeanDefinitionHolder,包含 BeanDefinition、Bean 的名稱以及別名(支援多個)

總結一下,BeanDefinition 介面的實現類主要根據 Bean 的定義方式進行區分,如下:

  1. XML 定義 Bean >>>>> GenericBeanDefinition

  2. @Component 以及派生註解定義 Bean >>>>> ScannedGenericBeanDefinition

  3. 藉助於 @Import 匯入 Bean >>>>> AnnotatedGenericBeanDefinition

  4. @Bean 定義的方法 >>>>> ConfigurationClassBeanDefinition

  5. 在 Spring BeanFactory 初始化 Bean 的前階段,會根據 BeanDefinition 生成一個合併後的 RootBeanDefinition 物件

BeanDefinition 介面

org.springframework.beans.factory.config.BeanDefinition 介面,繼承 AttributeAccessor 和 BeanMetadataElement 兩個介面,定義一個 Bean 的元資訊

BeanDefinition 內部就定義了獲取一些基礎元資訊的方法,可跳轉 BeanDefinition.java 檢視

AbstractBeanDefinition 抽象類

org.springframework.beans.factory.support.AbstractBeanDefinition 抽象類,實現 BeanDefinition 介面,繼承 BeanMetadataAttributeAccessor 類(AttributeAccessor 和 BeanMetadataElement 的實現類),包含了一個 Bean 幾乎所有的元資訊,可跳轉 AbstractBeanDefinition.java 檢視,下面列舉最常見的屬性:

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {

	@Nullable
	private volatile Object beanClass;

	private boolean abstractFlag = false;

	private boolean lazyInit = false;

	@Nullable
	private String[] dependsOn;

	private boolean primary = false;

	private boolean nonPublicAccessAllowed = true;

	@Nullable
	private String factoryBeanName;

	@Nullable
	private String factoryMethodName;

	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;

	@Nullable
	private MutablePropertyValues propertyValues;

	@Nullable
	private String initMethodName;

	@Nullable
	private String destroyMethodName;

	private boolean enforceInitMethod = true;

	private boolean enforceDestroyMethod = true;

	private boolean synthetic = false;

	private int role = BeanDefinition.ROLE_APPLICATION;

	@Nullable
	private String description;

	@Nullable
	private Resource resource;
    
    // ... 省略大量程式碼
}

GenericBeanDefinition

org.springframework.beans.factory.support.GenericBeanDefinition,繼承 AbstractBeanDefinition 抽象類,多了一個 parentName,表示有繼承關係,是一個標準 Bean 元資訊物件,通過 XML 定義的 Bean 會解析成該物件,程式碼如下:

public class GenericBeanDefinition extends AbstractBeanDefinition {

	@Nullable
	private String parentName;

	public GenericBeanDefinition() {
		super();
	}

	public GenericBeanDefinition(BeanDefinition original) {
		super(original);
	}
    
    // ... 省略相關方法
}

AnnotatedBeanDefinition 介面

org.springframework.beans.factory.annotation.AnnotatedBeanDefinition 介面,繼承 BeanDefinition 介面,定義註解類的元資訊,程式碼如下:

public interface AnnotatedBeanDefinition extends BeanDefinition {

	/**
	 * Obtain the annotation metadata (as well as basic class metadata)
	 * for this bean definition's bean class.
	 * @return the annotation metadata object (never {@code null})
	 */
	AnnotationMetadata getMetadata();

	/**
	 * Obtain metadata for this bean definition's factory method, if any.
	 * @return the factory method metadata, or {@code null} if none
	 * @since 4.1.1
	 */
	@Nullable
	MethodMetadata getFactoryMethodMetadata();

}

AnnotationMetadata 可以獲取到定義 Bean 的所有資訊,在 Spring 底層會通過 ASM(一個操作 Java 位元組碼與分析的框架)實現的

MethodMetadata 可以獲取到工廠方法的元資訊,目前我沒發現哪裡使用到

例如通過 @Component 註解定義的 Bean,那麼 AnnotationMetadata 可以獲取到這個 Class 物件的所有資訊

ScannedGenericBeanDefinition

org.springframework.context.annotation.ScannedGenericBeanDefinition,繼承 GenericBeanDefinition,實現 AnnotatedBeanDefinition 介面,多了一個 AnnotationMetadata 註解類元資訊物件,例如通過 @Component 註解定義的 Bean 會解析成該物件,程式碼如下:

public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

	private final AnnotationMetadata metadata;

	public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
		Assert.notNull(metadataReader, "MetadataReader must not be null");
		this.metadata = metadataReader.getAnnotationMetadata();
		setBeanClassName(this.metadata.getClassName());
	}

	@Override
	public final AnnotationMetadata getMetadata() {
		return this.metadata;
	}

	@Override
	@Nullable
	public MethodMetadata getFactoryMethodMetadata() {
		return null;
	}
}

AnnotatedGenericBeanDefinition

org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition,繼承 GenericBeanDefinition,實現 AnnotatedBeanDefinition 介面,和 ScannedGenericBeanDefinition 類似,程式碼如下:

public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

	private final AnnotationMetadata metadata;

	@Nullable
	private MethodMetadata factoryMethodMetadata;

	public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
		setBeanClass(beanClass);
		this.metadata = new StandardAnnotationMetadata(beanClass, true);
	}

	public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) {
		Assert.notNull(metadata, "AnnotationMetadata must not be null");
		if (metadata instanceof StandardAnnotationMetadata) {
			setBeanClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
		}
		else {
			setBeanClassName(metadata.getClassName());
		}
		this.metadata = metadata;
	}

	public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata, MethodMetadata factoryMethodMetadata) {
		this(metadata);
		Assert.notNull(factoryMethodMetadata, "MethodMetadata must not be null");
		setFactoryMethodName(factoryMethodMetadata.getMethodName());
		this.factoryMethodMetadata = factoryMethodMetadata;
	}

	@Override
	public final AnnotationMetadata getMetadata() {
		return this.metadata;
	}

	@Override
	@Nullable
	public final MethodMetadata getFactoryMethodMetadata() {
		return this.factoryMethodMetadata;
	}

}

通過 @Import 匯入的 Configuration Class 會解析成該物件,不過 factoryMethodMetadata 還是為 null

RootBeanDefinition

org.springframework.beans.factory.support.RootBeanDefinition,繼承 AbstractBeanDefinition 抽象類,表示合併後的 BeanDefinition 物件

在 Spring BeanFactory 初始化 Bean 的前階段,會根據 BeanDefinition 生成一個 RootBeanDefinition(具有層次性則會進行合併),用於後續例項化和初始化

可跳轉 RootBeanDefinition.java 檢視

BeanDefinitionHolder

org.springframework.beans.factory.config.BeanDefinitionHolder,包含 BeanDefinition、Bean 的名稱以及別名(支援多個),程式碼如下:

public class BeanDefinitionHolder implements BeanMetadataElement {

   private final BeanDefinition beanDefinition;

   private final String beanName;

   @Nullable
   private final String[] aliases;

   public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
      this(beanDefinition, beanName, null);
   }

   public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
      Assert.notNull(beanDefinition, "BeanDefinition must not be null");
      Assert.notNull(beanName, "Bean name must not be null");
      this.beanDefinition = beanDefinition;
      this.beanName = beanName;
      this.aliases = aliases;
   }
}

在解析出來 BeanDefinition 後都會轉換成 BeanDefinitionHolder 物件,然後進行註冊

總結

Spring Bean 的“前身”為 BeanDefinition 物件,裡面包含了 Bean 的元資訊,後續在 Bean 的生命週期中會根據該物件進行例項化和初始化等工作

BeanDefinition 介面的實現類主要根據 Bean 的定義方式進行區分,如下:

  1. XML 定義 Bean:GenericBeanDefinition

  2. @Component 以及派生註解定義 Bean:ScannedGenericBeanDefinition

  3. 藉助於 @Import 匯入 Bean:AnnotatedGenericBeanDefinition

  4. @Bean 定義的方法:ConfigurationClassBeanDefinition 私有靜態類

上面的 123 三種 BeanDefinition 實現類具有層次性,在 Spring BeanFactory 初始化 Bean 的前階段,會根據 BeanDefinition 生成一個合併後的 RootBeanDefinition 物件

相關文章