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 ,每種BD在spring中的作用不同
- RootBeanDefinition
這個最簡單的解釋就是在spring中源碼中涉及到合併BeanDefinition的時候返回的值都是使用RootBeanDefinition來接受。所以我們可以先理解爲它表示合併後的BeanDefinition信息。
- ChildBeanDefinition
看名稱我們就能知道這個BeanDefinition應該來表示子的關係,那誰來表示父BD呢?我們可以通過setParentName來手動設置一個父。因爲ChildBeanDefinition的構造方法都必須填ParentName所以他只能表示子的概念。
通過javaDoc發現這個類從2.5開始就廢棄了推薦使用GenericBeanDefinition
- GenericBeanDefinition
GenericBeanDefinition可以理解爲一個通用的BD,因爲他既可以表示父也可以表示子,並且javadoc中也明確表示推薦使用。但是爲什麼不能完全替代RootBeanDefinition?還是如上邊說的,因爲spring源碼中寫死了
- AnnotatedBeanDefinition
這個相當於一個標示入口,通過註解編程的spring項目,會以這個bean爲入口解析配置類
- AnnotatedGenericBeanDefinition
在解析時是註解的類形成的BeanDefinition
- ScannedGenericBeanDefinition
在解析時通過scan掃描出來的類形成的BeanDefinition
- 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,再次執行代碼發現還是可以正常的注入進來。