Spring源碼解析之BeanDefinition

通過閱讀本文你能獲得什麼

  • 1、什麼是BeanDefinition
  • 2、怎麼用BeanDefinition定製化開發

爲什麼要學習BeanDefinition

BeanDefinition看名稱可以大致明白是用來描述spring中bean的信息。
如果把spring創建bean類比成工廠生產糖果,工廠生產糖果需要有原材料,沒有原材料就沒辦法生產。那BeanDefinition就是spring創建bean的原材料。可以發現BeanDefinition對整個spring工廠 來說是多麼重要,基於這個願意,給大家分享一下最近學習的BeanDefinition相關的知識

BeanDefinition的定義

上邊是類比了一個例子,說BeanDefinition工廠的原材料,但是對於還沒有看過Spring源碼的人來說,可能還不是很明白。那我這裏爲大家畫一張圖,來解釋一下。

再來看一下源碼中對BD的解釋

/**
 * A BeanDefinition describes a bean instance, which has property values,
 * constructor argument values, and further information supplied by
 * concrete implementations.
 */
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {}

從上邊的注視中我們可以看到A BeanDefinition describes a bean instance, which has property values ,constructor argument values, and further information supplied by concrete implementations.這句話 翻譯過來就是BeanDefinition描述了一個bean實例,該實例具有屬性值,構造函數參數值以及具體實現所提供的更多信息。

AttributeAccessor於BeanMetadataElement 這兩個接口會在下邊進行介紹

BeanDefinition的類型

先來看一張BeanDefinition的繼承關係
BeanDefinition

從上圖中我們看到有以下幾個主要的BeanDefinition ,每種BD在spring中的作用不同

  1. RootBeanDefinition

這個最簡單的解釋就是在spring中源碼中涉及到合併BeanDefinition的時候返回的值都是使用RootBeanDefinition來接受。所以我們可以先理解爲它表示合併後的BeanDefinition信息。

  1. ChildBeanDefinition

看名稱我們就能知道這個BeanDefinition應該來表示子的關係,那誰來表示父BD呢?我們可以通過setParentName來手動設置一個父。因爲ChildBeanDefinition的構造方法都必須填ParentName所以他只能表示子的概念。
通過javaDoc發現這個類從2.5開始就廢棄了推薦使用GenericBeanDefinition

  1. GenericBeanDefinition

GenericBeanDefinition可以理解爲一個通用的BD,因爲他既可以表示父也可以表示子,並且javadoc中也明確表示推薦使用。但是爲什麼不能完全替代RootBeanDefinition?還是如上邊說的,因爲spring源碼中寫死了

  1. AnnotatedBeanDefinition

這個相當於一個標示入口,通過註解編程的spring項目,會以這個bean爲入口解析配置類

  1. AnnotatedGenericBeanDefinition

在解析時是註解的類形成的BeanDefinition

  1. ScannedGenericBeanDefinition

在解析時通過scan掃描出來的類形成的BeanDefinition

  1. ConfigurationClassBeanDefinition

標註了@Bean方法創建的beanDefinition

後邊這四種BeanDefinition可以理解爲自身帶有屬性因爲的BeanDefinition,因爲源碼中有的會判斷instance of 哪個類型來做不同的邏輯處理

BeanDefinition的屬性的意義

因爲屬性比較多,所以直接當作注視寫在代碼中

package org.springframework.beans.factory.config;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.AttributeAccessor;
import org.springframework.lang.Nullable;

/**
 * A BeanDefinition describes a bean instance, which has property values,
 * constructor argument values, and further information supplied by
 * concrete implementations.
 *
 * <p>This is just a minimal interface: The main intention is to allow a
 * {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer}
 * to introspect and modify property values and other bean metadata.
 *
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 19.03.2004
 * @see ConfigurableListableBeanFactory#getBeanDefinition
 * @see org.springframework.beans.factory.support.RootBeanDefinition
 * @see org.springframework.beans.factory.support.ChildBeanDefinition
 */
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	/**
	 * beanDefinition 中的常量值 代表 原型 主要是方便我們程序員在外部調用可以直接使用
	 * Scope identifier for the standard singleton scope: "singleton".
	 * <p>Note that extended bean factories might support further scopes.
	 * @see #setScope
	 */
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

	/**
	 * beanDefinition 中的常量值 代表 原型 主要是方便我們程序員在外部調用可以直接使用
	 * Scope identifier for the standard prototype scope: "prototype".
	 * <p>Note that extended bean factories might support further scopes.
	 * @see #setScope
	 */
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


	/**
	 * 來標示這個BeanDefinition是應用程序的主要部分通常對應於用戶定義的bean
	 * Role hint indicating that a {@code BeanDefinition} is a major part
	 * of the application. Typically corresponds to a user-defined bean.
	 */
	int ROLE_APPLICATION = 0;

	/**
	 * 相對於 ROLE_APPLICATION 而言 ROLE_SUPPORT 不是當前應用的主要部分
	 * Role hint indicating that a {@code BeanDefinition} is a supporting
	 * part of some larger configuration, typically an outer
	 * {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
	 * {@code SUPPORT} beans are considered important enough to be aware
	 * of when looking more closely at a particular
	 * {@link org.springframework.beans.factory.parsing.ComponentDefinition},
	 * but not when looking at the overall configuration of an application.
	 */
	int ROLE_SUPPORT = 1;

	/**
	 * BeanDefinition 來表式完全在內部起作用的bean 不爲我們外部程序員使用
	 * Role hint indicating that a {@code BeanDefinition} is providing an
	 * entirely background role and has no relevance to the end-user. This hint is
	 * used when registering beans that are completely part of the internal workings
	 * of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
	 */
	int ROLE_INFRASTRUCTURE = 2;


	// Modifiable attributes

	/**
	 * 如果需要給當前的設置BD設置父定義 則通過這個方法來設置
	 * 結合abstract 來看
	 * 當前beanDefinition會繼承父BD中的信息 子BD有則覆蓋
	 * Set the name of the parent definition of this bean definition, if any.
	 */
	void setParentName(@Nullable String parentName);

	/**
	 *	獲取設置的父定義的名稱
	 * Return the name of the parent definition of this bean definition, if any.
	 */
	@Nullable
	String getParentName();

	/**
	 * 設置bean的class name   實例化的使用使用的類型
	 * Specify the bean class name of this bean definition.
	 * <p>The class name can be modified during bean factory post-processing,
	 * typically replacing the original class name with a parsed variant of it.
	 * @see #setParentName
	 * @see #setFactoryBeanName
	 * @see #setFactoryMethodName
	 */
	void setBeanClassName(@Nullable String beanClassName);

	/**
	 * 當前Bean的類名稱,全路徑名。該名稱可以在自定義的BeanFactoryPostProcessor中進行修改
	 * mybatis 結合factoryBean 時就修改了這個屬性
	 *
	 * Return the current bean class name of this bean definition.
	 * <p>Note that this does not have to be the actual class name used at runtime, in
	 * case of a child definition overriding/inheriting the class name from its parent.
	 * Also, this may just be the class that a factory method is called on, or it may
	 * even be empty in case of a factory bean reference that a method is called on.
	 * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
	 * rather only use it for parsing purposes at the individual bean definition level.
	 * @see #getParentName()
	 * @see #getFactoryBeanName()
	 * @see #getFactoryMethodName()
	 */
	@Nullable
	String getBeanClassName();

	/**
	 * 作用域
	 * 來表示bean 是單例還是原型
	 * Override the target scope of this bean, specifying a new scope name.
	 * @see #SCOPE_SINGLETON
	 * @see #SCOPE_PROTOTYPE
	 */
	void setScope(@Nullable String scope);

	/**
	 * 返回作用域
	 * Return the name of the current target scope for this bean,
	 * or {@code null} if not known yet.
	 */
	@Nullable
	String getScope();

	/**
	 * 設置是否懶加載
	 * Set whether this bean should be lazily initialized.
	 * <p>If {@code false}, the bean will get instantiated on startup by bean
	 * factories that perform eager initialization of singletons.
	 */
	void setLazyInit(boolean lazyInit);

	/**
	 * 返回否懶加載
	 * Return whether this bean should be lazily initialized, i.e. not
	 * eagerly instantiated on startup. Only applicable to a singleton bean.
	 */
	boolean isLazyInit();

	/**
	 * 設置當前Bean要依賴於@DependsOn的這些Bean都實例化之後才能進行實例化
	 * Set the names of the beans that this bean depends on being initialized.
	 * The bean factory will guarantee that these beans get initialized first.
	 */
	void setDependsOn(@Nullable String... dependsOn);

	/**
	 * 返回依賴的bean的名稱
	 * Return the bean names that this bean depends on.
	 */
	@Nullable
	String[] getDependsOn();

	/**
	 * 設置是否作爲自動裝配的候選對象   僅影響按類型注入
	 * by type注入  且autowireCandidate設置爲true纔會注入成功   其他bean依賴這個bean 纔會注入成功
	 * Set whether this bean is a candidate for getting autowired into some other bean.
	 * <p>Note that this flag is designed to only affect type-based autowiring.
	 * It does not affect explicit references by name, which will get resolved even
	 * if the specified bean is not marked as an autowire candidate. As a consequence,
	 * autowiring by name will nevertheless inject a bean if the name matches.
	 */
	void setAutowireCandidate(boolean autowireCandidate);

	/**
	 * 返回是否作爲類型注入時的候選Bean
	 * Return whether this bean is a candidate for getting autowired into some other bean.
	 */
	boolean isAutowireCandidate();

	/**
	 * 設置此bean是否爲自動裝配的主要候選對象
	 *
	 * 設置當前的Bean是優先被注入的,比如同一個類型使用兩個不同的beanName在Spring中存在兩個對象,
	 * 那麼被標記爲primary=true的在根據 by type注入的時候會優先被注入
	 * Set whether this bean is a primary autowire candidate.
	 * <p>If this value is {@code true} for exactly one bean among multiple
	 * matching candidates, it will serve as a tie-breaker.
	 */
	void setPrimary(boolean primary);

	/**
	 * Return whether this bean is a primary autowire candidate.
	 */
	boolean isPrimary();

	/**
	 * 設置FactoryBean的名稱  FactoryBean就是可以產生Spring Bean的Bean,通過beanName和&beanName分別得到Bean和FactoryBean實例
	 *
	 * Specify the factory bean to use, if any.
	 * This the name of the bean to call the specified factory method on.
	 * @see #setFactoryMethodName
	 */
	void setFactoryBeanName(@Nullable String factoryBeanName);

	/**
	 * Return the factory bean name, if any.
	 */
	@Nullable
	String getFactoryBeanName();

	/**
	 *
	 * 指定工廠方法(如果有)。將使用構造函數參數調用此方法,如果未指定任何參數,則不使用任何參數。該方法將在指定的工廠bean(如果有)上被調用,
	 * 否則將作爲本地bean類上的靜態方法被調用
	 *
	 * xml
	 * Specify a factory method, if any. This method will be invoked with
	 * constructor arguments, or with no arguments if none are specified.
	 * The method will be invoked on the specified factory bean, if any,
	 * or otherwise as a static method on the local bean class.
	 * @see #setFactoryBeanName
	 * @see #setBeanClassName
	 *
	 * 配置類中 @Bean 的靜態方法
	 */
	void setFactoryMethodName(@Nullable String factoryMethodName);

	/**
	 * Return a factory method, if any.
	 */
	@Nullable
	String getFactoryMethodName();

	/**
	 * 返回構造函數的參數,可以使用BeanFactoryPostProcessor修改參數
	 * Return the constructor argument values for this bean.
	 * <p>The returned instance can be modified during bean factory post-processing.
	 * @return the ConstructorArgumentValues object (never {@code null})
	 */
	ConstructorArgumentValues getConstructorArgumentValues();

	/**
	 * Return if there are constructor argument values defined for this bean.
	 * @since 5.0.2
	 */
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	/**
	 * 返回Bean的屬性對應的值,可以使用BeanFactoryPostProcessor修改這些值
	 *
	 * Return the property values to be applied to a new instance of the bean.
	 * <p>The returned instance can be modified during bean factory post-processing.
	 * @return the MutablePropertyValues object (never {@code null})
	 */
	MutablePropertyValues getPropertyValues();

	/**
	 * Return if there are property values values defined for this bean.
	 * @since 5.0.2
	 */
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	/**
	 * 當前Bean中添加一個初始化方法,再使用 BeanFactoryPostProcessor 設置它的BeanDefinition initMethodName
	 * Set the name of the initializer method.
	 * @since 5.1
	 */
	void setInitMethodName(@Nullable String initMethodName);

	/**
	 * Return the name of the initializer method.
	 * @since 5.1
	 */
	@Nullable
	String getInitMethodName();

	/**
	 * 設置Bean被銷燬時要執行的方法,可以做一些清理工作
	 * Set the name of the destroy method.
	 * @since 5.1
	 */
	void setDestroyMethodName(@Nullable String destroyMethodName);

	/**
	 * Return the name of the destroy method.
	 * @since 5.1
	 */
	@Nullable
	String getDestroyMethodName();

	/**
	 * 設角色
	 * Set the role hint for this {@code BeanDefinition}. The role hint
	 * provides the frameworks as well as tools with an indication of
	 * the role and importance of a particular {@code BeanDefinition}.
	 * @since 5.1
	 * @see #ROLE_APPLICATION
	 * @see #ROLE_SUPPORT
	 * @see #ROLE_INFRASTRUCTURE
	 */
	void setRole(int role);

	/**
	 * Get the role hint for this {@code BeanDefinition}. The role hint
	 * provides the frameworks as well as tools with an indication of
	 * the role and importance of a particular {@code BeanDefinition}.
	 * @see #ROLE_APPLICATION
	 * @see #ROLE_SUPPORT
	 * @see #ROLE_INFRASTRUCTURE
	 */
	int getRole();

	/**
	 * 設置此bean定義的可讀描述  可讀
	 * Set a human-readable description of this bean definition.
	 * @since 5.1
	 */
	void setDescription(@Nullable String description);

	/**
	 * Return a human-readable description of this bean definition.
	 */
	@Nullable
	String getDescription();


	// Read-only attributes

	/**
	 * Return whether this a <b>Singleton</b>, with a single, shared instance
	 * returned on all calls.
	 * @see #SCOPE_SINGLETON
	 */
	boolean isSingleton();

	/**
	 * Return whether this a <b>Prototype</b>, with an independent instance
	 * returned for each call.
	 * @since 3.0
	 * @see #SCOPE_PROTOTYPE
	 */
	boolean isPrototype();

	/**
	 * 返回對象是否是抽象的,用於校驗BeanDefinition是否符合Spring的規則
	 *
	 * 爲什麼?  主要是作爲模版  被別人來繼承
	 * 官網描述  https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/core.html#beans-child-bean-definitions
	 * xml中用的比較多   很多類的屬性一樣  可以定義一個模版  其他的把parent設置成這個模版的beanName
	 * 	RootBeanDefinition rootBD=  new RootBeanDefinition();
	 * 	rootBD.setAbstract(true); 可以不設置beanClass
	 *  ctx.registorBeanDefinition("rb",rootBD);
	 * 	ChildBeanDefinition cb = new ChildBeanDefinition("rb");
	 * Return whether this bean is "abstract", that is, not meant to be instantiated.
	 */
	boolean isAbstract();



	/**
	 * 不重要  試了幾個自定義的BD 都爲null  有興趣可以去查一下
	 *
	 * Return a description of the resource that this bean definition
	 * came from (for the purpose of showing context in case of errors).
	 */
	@Nullable
	String getResourceDescription();

	/**
	 * 不重要  試了幾個自定義的BD 都爲null  有興趣可以去查一下
	 * Return the originating BeanDefinition, or {@code null} if none.
	 * Allows for retrieving the decorated bean definition, if any.
	 * <p>Note that this method returns the immediate originator. Iterate through the
	 * originator chain to find the original BeanDefinition as defined by the user.
	 */
	@Nullable
	BeanDefinition getOriginatingBeanDefinition();
}

  • AutowireCandidate 只會影響BY_TYPE 的依賴注入 下邊進行證明

BeanDefinition的實戰

怎麼手動創建一個BeanDefinition

程序員怎麼手動創建一個BeanDefinition

直接通過new來創建

上邊介紹了很多種BeanDefinition 我們用的時候一般使用GenericBeanDefinition。

public class MyBeanDefinitionTests {

	@Test
	public void bdCreate() {
		
		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(Tench.class);
		genericBeanDefinition.setLazyInit(true);
		genericBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

	}
}

通過Builder來創建

public class MyBeanDefinitionTests {

	@Test
	public void bdCreate() {
		
		GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder.genericBeanDefinition(Tench.class).
				setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
				.getBeanDefinition();

	}
}

怎麼把手動創建的BeanDefinition放到容器中

通過context

public class MyBeanDefinitionTests {

	AnnotationConfigApplicationContext applicationContext = null;

	@Before
	public void before() {
		applicationContext = new AnnotationConfigApplicationContext();
	}

	@Test
	public void bdCreate() {

		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(Tench.class);
		applicationContext.registerBeanDefinition("tench", genericBeanDefinition);
	}
	}

通過ImportBeanDefinitionRegistrar

public class MyBeanDefinitionTests {

	AnnotationConfigApplicationContext applicationContext = null;

	@Before
	public void before() {
		applicationContext = new AnnotationConfigApplicationContext();
	}

	@Test
	public void bdCreate() {
		applicationContext.register(MyImportBeanDefinitionRegistrar.class);
		applicationContext.refresh();
	}
	}
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(Tench.class);
		registry.registerBeanDefinition("tench", genericBeanDefinition);
	}
}

在實際開發中我們用這種方式比較多,不如mybatis和spring整合過程。

怎麼修改一個BeanDefinition

通過BeanFactoryPostProcessor

public class MyBeanDefinitionTests {

	AnnotationConfigApplicationContext applicationContext = null;

	@Before
	public void before() {
		applicationContext = new AnnotationConfigApplicationContext();
	}

	@Test
	public void bdCreate() {
		applicationContext.register(MyBeanFactoryPostProcessor.class);
		applicationContext.refresh();
	}
	}
	class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		BeanDefinition tench = beanFactory.getBeanDefinition("tench");
		tench.setLazyInit(false);
	}
}

這種方式使用的較多

通過ImportBeanDefinitionRegistrar

MyImportBeanDefinitionRegistrar 修改代碼如下

class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinition tench = registry.getBeanDefinition("tench");
		tench.setLazyInit(false);
	}
}

怎麼進行父子定義信息的繼承和覆蓋

通過setParentName

還記得ParentName這個屬性嗎?上邊只是介紹了屬性的作用,但是沒有代碼演示,現在給大家進行一下代碼演示
首先我們需要兩個bd

	@Test
	public void testAbstractBD() {

		RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
		rootBeanDefinition.setAbstract(true);
		rootBeanDefinition.setBeanClass(Parent.class);
		rootBeanDefinition.getPropertyValues().add("name", 123);
		rootBeanDefinition.getPropertyValues().add("role", 123);

		ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("parent");
		childBeanDefinition.setBeanClass(Child.class);
		childBeanDefinition.getPropertyValues().add("name", 456);
		childBeanDefinition.getPropertyValues().add("value", 456);

		applicationContext.registerBeanDefinition("parent", rootBeanDefinition);

		applicationContext.registerBeanDefinition("child", childBeanDefinition);

		applicationContext.refresh();

		Object child = applicationContext.getBean("child");

		System.out.println("child:" + child);

	}

程序打印的結果是 child:Child{name='456', role='123', value='456'}
可以看到子類中定義了則進行覆蓋。沒有定義則進行繼承

證明AutowireCandidate只在BY_TYPE下有作用

public class MyBeanDefinitionTests {

	AnnotationConfigApplicationContext applicationContext = null;

	@Before
	public void before() {
		applicationContext = new AnnotationConfigApplicationContext();
	}

	@Test
	public void setAutowireCandidate() {
		GenericBeanDefinition tenchBeanDefinition = new GenericBeanDefinition();
		//是否是候選注入
		tenchBeanDefinition.setAutowireCandidate(false);
		tenchBeanDefinition.setBeanClass(Tench.class);

		GenericBeanDefinition zoomBeanDefinition = new GenericBeanDefinition();
		zoomBeanDefinition.setBeanClass(Zoom.class);

		zoomBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//		zoomBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);

		applicationContext.registerBeanDefinition("zoom", zoomBeanDefinition);
		applicationContext.registerBeanDefinition("tench", tenchBeanDefinition);

		applicationContext.refresh();

		Zoom zoom = applicationContext.getBean("zoom", Zoom.class);
		System.out.println(zoom.toString());

	}

}
class Zoom {

	private Tench tench;

	public void setTench(Tench tench) {
		this.tench = tench;
	}

	@Override
	public String toString() {
		return "tench ==" + tench;
	}
}

class Tench {

	private String name = "sss";

	@Override
	public String toString() {
		return name;
	}
}

上邊代碼的含義是 要把Tench 對象 注入到Zoom 對象中,平時開發的時候,我們一般會搭配@Autowire使用,這次我們直接通過類型注入zoomBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
代碼
執行代碼後結果 打印的 tench == null。這表明沒有注入進來。
現在把 tenchBeanDefinition.setAutowireCandidate(false);行代碼注視掉,會發現控制檯打印的是 tench == sss 。這個結果說明了AutowireCandidate的作用,不會被作爲候選bean進行注入。那怎麼驗證只在 BY_TYPE 下生效呢?很簡單我們設置zoom的裝配模型爲AbstractBeanDefinition.AUTOWIRE_BY_NAME,再次執行代碼發現還是可以正常的注入進來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章