BeanNameAware
一、基本信息
?? 作者 - Lex ?? 博客 - 我的CSDN ?? 文章目錄 - 所有文章 ?? 源碼地址 - BeanNameAware源碼
二、接口描述
BeanNameAware 接口。當一個 Bean 實現(xiàn)了此接口,可以感知其在 Spring 容器中的名稱。
三、接口源碼
BeanNameAware 是 Spring 框架自 01.11.2003 開始引入的一個核心接口。實現(xiàn)BeanNameAware接口的對象會在Spring容器中被自動注入Bean的名稱。
/**
* 由希望知道其在 bean 工廠中名稱的 beans 實現(xiàn)的接口。
* 注意通常不推薦一個對象依賴于其 bean 名稱,因為這可能導致對外部配置的脆弱依賴,
* 以及可能的不必要的對 Spring API 的依賴。
*
* 有關所有 bean 生命周期方法的列表,請參見
* BeanFactory BeanFactory javadocs。
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 01.11.2003
* @see BeanClassLoaderAware
* @see BeanFactoryAware
* @see InitializingBean
*/
public interface BeanNameAware extends Aware {
/**
* 設置在創(chuàng)建此 bean 的 bean 工廠中的 bean 的名稱。
* 此方法在填充常規(guī) bean 屬性之后被調用,但在如 InitializingBean#afterPropertiesSet() 這樣的
* 初始化回調或自定義初始化方法之前被調用。
* @param name 工廠中的 bean 的名稱。注意,這個名稱是工廠中使用的實際 bean 名稱,
* 這可能與最初指定的名稱不同:尤其對于內部 bean 名稱,實際的 bean 名稱可能已通過添加 "#..." 后綴變得唯一。
* 如果需要,可以使用 BeanFactoryUtils#originalBeanName(String) 方法來提取沒有后綴的原始 bean 名稱。
*/
void setBeanName(String name);
}
四、主要功能
-
提供
setBeanName方法- 當一個 Bean 實現(xiàn)了
BeanNameAware接口,它需要提供setBeanName方法的實現(xiàn)。這個方法有一個參數,即該 Bean 在 Spring 容器中的名稱。
- 當一個 Bean 實現(xiàn)了
-
自動回調
- 當 Spring 容器創(chuàng)建并配置一個實現(xiàn)了
BeanNameAware接口的 Bean 時,容器會自動回調setBeanName方法,并傳入該 Bean 在容器中的名稱。這意味著我們不需要顯式地調用這個方法;Spring 容器會自動處理。
- 當 Spring 容器創(chuàng)建并配置一個實現(xiàn)了
-
獲取 Bean 的名稱
- 有時,Bean 可能需要知道其在容器中的名稱以執(zhí)行特定的邏輯或功能,或者為了日志記錄或其他目的。通過實現(xiàn)
BeanNameAware,Bean 可以輕松獲得此信息。
- 有時,Bean 可能需要知道其在容器中的名稱以執(zhí)行特定的邏輯或功能,或者為了日志記錄或其他目的。通過實現(xiàn)
五、最佳實踐
首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式),構造參數我們給定了一個MyConfiguration組件類。
public class BeanNameAwareApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
}
}
使用@ComponentScan注解,告訴 Spring 容器去 "com.xcs.spring.service" 掃描包及其子包
@Configuration
@ComponentScan("com.xcs.spring.service")
public class MyConfiguration {
}
MyBasePayService 是一個抽象類,結合了 Spring 的三個特殊接口:BeanNameAware(讓 Bean 知道其名字)、InitializingBean(Bean 屬性設置后的初始化操作)和 DisposableBean(Bean 銷毀前的操作)。
public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean {
private String beanName;
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Module " + beanName + " has been registered.");
}
@Override
public void destroy() throws Exception {
System.out.println("Module " + beanName + " has been unregistered.");
}
}
MyAliPayService 和 MyWeChatPayService 是兩個支付服務類,都繼承自 MyBasePayService,因此它們會自動獲得與 Spring 容器生命周期相關的基本功能。這種設計方式為多個支付服務提供了一個共同的生命周期管理模式,同時允許每個服務添加其特定的支付邏輯。
@Service
public class MyAliPayService extends MyBasePayService{
}
@Service
public class MyWeChatPayService extends MyBasePayService{
}
運行結果發(fā)現(xiàn),當 Spring 容器啟動并初始化 Beans 時,它正確地識別并實例化了 MyAliPayService 和 MyWeChatPayService 這兩個服務。由于這兩個服務都繼承自 MyBasePayService,在 Bean 的屬性被設置之后(即在 afterPropertiesSet() 方法中),它們分別打印出了 "Module myAliPayService has been registered." 和 "Module myWeChatPayService has been registered." 這兩條信息,提供了一個共同的生命周期管理模式。
Module myAliPayService has been registered.
Module myWeChatPayService has been registered.
六、時序圖
sequenceDiagram
Title: BeanNameAware時序圖
participant BeanNameAwareApplication
participant AnnotationConfigApplicationContext
participant AbstractApplicationContext
participant DefaultListableBeanFactory
participant AbstractBeanFactory
participant DefaultSingletonBeanRegistry
participant AbstractAutowireCapableBeanFactory
participant MyBasePayService
BeanNameAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)<br>創(chuàng)建上下文
AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()<br>刷新上下文
AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)<br>初始化Bean工廠
AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()<br>實例化單例
DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)<br>獲取Bean
AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)<br>執(zhí)行獲取Bean
AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)<br>獲取單例Bean
DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()<br>獲取Bean實例
AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)<br>創(chuàng)建Bean
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)<br>執(zhí)行Bean創(chuàng)建
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)<br>負責bean的初始化
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)<br>調用Aware方法
AbstractAutowireCapableBeanFactory->>MyBasePayService:setBeanName(beanName)<br>設置Bean名稱
AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean對象
AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean對象
AnnotationConfigApplicationContext-->>BeanNameAwareApplication:初始化完成
七、源碼分析
首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式),構造參數我們給定了一個MyConfiguration組件類。
public class BeanNameAwareApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
}
}
在org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext構造函數中,執(zhí)行了三個步驟,我們重點關注refresh()方法
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
在org.springframework.context.support.AbstractApplicationContext#refresh方法中我們重點關注一下finishBeanFactoryInitialization(beanFactory)這方法會對實例化所有剩余非懶加載的單列Bean對象,其他方法不是本次源碼閱讀的重點暫時忽略。
@Override
public void refresh() throws BeansException, IllegalStateException {
// ... [代碼部分省略以簡化]
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// ... [代碼部分省略以簡化]
}
在org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization方法中,會繼續(xù)調用DefaultListableBeanFactory類中的preInstantiateSingletons方法來完成所有剩余非懶加載的單列Bean對象。
/**
* 完成此工廠的bean初始化,實例化所有剩余的非延遲初始化單例bean。
*
* @param beanFactory 要初始化的bean工廠
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// ... [代碼部分省略以簡化]
// 完成所有剩余非懶加載的單列Bean對象。
beanFactory.preInstantiateSingletons();
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法中,主要的核心目的是預先實例化所有非懶加載的單例bean。在Spring的上下文初始化完成后,該方法會被觸發(fā),以確保所有單例bean都被正確地創(chuàng)建并初始化。其中getBean(beanName)是此方法的核心操作。對于容器中定義的每一個單例bean,它都會調用getBean方法,這將觸發(fā)bean的實例化、初始化及其依賴的注入。如果bean之前沒有被創(chuàng)建過,那么這個調用會導致其被實例化和初始化。
public void preInstantiateSingletons() throws BeansException {
// ... [代碼部分省略以簡化]
// 循環(huán)遍歷所有bean的名稱
for (String beanName : beanNames) {
getBean(beanName);
}
// ... [代碼部分省略以簡化]
}
在org.springframework.beans.factory.support.AbstractBeanFactory#getBean()方法中,又調用了doGetBean方法來實際執(zhí)行創(chuàng)建Bean的過程,傳遞給它bean的名稱和一些其他默認的參數值。此處,doGetBean負責大部分工作,如查找bean定義、創(chuàng)建bean(如果尚未創(chuàng)建)、處理依賴關系等。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,首先檢查所請求的bean是否是一個單例并且已經創(chuàng)建。如果尚未創(chuàng)建,它將創(chuàng)建一個新的實例。在這個過程中,它處理可能的異常情況,如循環(huán)引用,并確保返回的bean是正確的類型。這是Spring容器bean生命周期管理的核心部分。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// ... [代碼部分省略以簡化]
// 開始創(chuàng)建bean實例
if (mbd.isSingleton()) {
// 如果bean是單例的,我們會嘗試從單例緩存中獲取它
// 如果不存在,則使用lambda創(chuàng)建一個新的實例
sharedInstance = getSingleton(beanName, () -> {
try {
// 嘗試創(chuàng)建bean實例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ... [代碼部分省略以簡化]
}
});
// 對于某些bean(例如FactoryBeans),可能需要進一步處理以獲取真正的bean實例
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ... [代碼部分省略以簡化]
// 確保返回的bean實例與請求的類型匹配
return adaptBeanInstance(name, beanInstance, requiredType);
}
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()方法中,主要負責從單例緩存中獲取一個已存在的bean實例,或者使用提供的ObjectFactory創(chuàng)建一個新的實例。這是確保bean在Spring容器中作為單例存在的關鍵部分。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 斷言bean名稱不能為空
Assert.notNull(beanName, "Bean name must not be null");
// 同步訪問單例對象緩存,確保線程安全
synchronized (this.singletonObjects) {
// 從緩存中獲取單例對象
Object singletonObject = this.singletonObjects.get(beanName);
// 如果緩存中沒有找到
if (singletonObject == null) {
// ... [代碼部分省略以簡化]
try {
// 使用工廠創(chuàng)建新的單例實例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// ... [代碼部分省略以簡化]
}
catch (BeanCreationException ex) {
// ... [代碼部分省略以簡化]
}
finally {
// ... [代碼部分省略以簡化]
}
// ... [代碼部分省略以簡化]
}
// 返回單例對象
return singletonObject;
}
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()方法中,主要的邏輯是調用 doCreateBean,這是真正進行 bean 實例化、屬性填充和初始化的地方。這個方法會返回新創(chuàng)建的 bean 實例。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... [代碼部分省略以簡化]
try {
// 正常的bean實例化、屬性注入和初始化。
// 這里是真正進行bean創(chuàng)建的部分。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
// 記錄bean成功創(chuàng)建的日志
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// ... [代碼部分省略以簡化]
}
catch (Throwable ex) {
// ... [代碼部分省略以簡化]
}
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,initializeBean方法是bean初始化,確保bean是完全配置和準備好的。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 聲明一個對象,后續(xù)可能用于存放初始化后的bean或它的代理對象
Object exposedObject = bean;
// ... [代碼部分省略以簡化]
try {
// ... [代碼部分省略以簡化]
// bean初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ... [代碼部分省略以簡化]
}
// 返回創(chuàng)建和初始化后的bean
return exposedObject;
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean方法中,invokeAwareMethods(beanName, bean)是一個非常重要的步驟。這個方法是為了處理實現(xiàn)了Spring的Aware接口族的Beans(例如BeanNameAware, BeanFactoryAware等)。如果提供的bean實現(xiàn)了任何這些接口,該方法會回調相應的Aware方法。
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// ... [代碼部分省略以簡化]
invokeAwareMethods(beanName, bean);
// ... [代碼部分省略以簡化]
return wrappedBean;
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods方法中,用于處理實現(xiàn)了 Spring Aware 接口族的 beans。當一個 bean 實現(xiàn)了如 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 等接口時,此方法確保正確的回調方法被調用,從而為 bean 提供關于其運行環(huán)境或其他相關信息。
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
// ... [代碼部分省略以簡化]
}
if (bean instanceof BeanFactoryAware) {
// ... [代碼部分省略以簡化]
}
}
}
最后執(zhí)行到我們自定義的邏輯中,我們將這個名稱存儲在 beanName 變量中,以便后續(xù)使用。
public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean {
private String beanName;
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Module " + beanName + " has been registered.");
}
@Override
public void destroy() throws Exception {
System.out.println("Module " + beanName + " has been unregistered.");
}
}
八、注意事項
-
與其他生命周期方法的順序
-
setBeanName方法的調用是在其他許多生命周期方法之前的,例如InitializingBean#afterPropertiesSet和任何定義的初始化方法。因此,我們不應該在setBeanName方法內部預期其他配置或初始化邏輯已經完成。
-
-
僅在容器管理的 Beans 中有效
- 只有當 bean 是由 Spring 容器管理時,
BeanNameAware才會生效。簡單地創(chuàng)建一個類的實例(例如通過new關鍵字)并不會觸發(fā)BeanNameAware功能。
- 只有當 bean 是由 Spring 容器管理時,
-
與其他 Aware 接口的組合使用
- 當一個 bean 同時實現(xiàn)多個
Aware接口時,需要注意它們的調用順序。例如,BeanNameAware、BeanFactoryAware和ApplicationContextAware的回調方法調用順序是固定的。
- 當一個 bean 同時實現(xiàn)多個
-
Bean 名稱的唯一性
- Spring 容器內的 bean 名稱是唯一的,但如果使用別名,同一個 bean 可能會有多個名稱。當實現(xiàn)
BeanNameAware時,我們獲得的是 bean 的主要名稱。
- Spring 容器內的 bean 名稱是唯一的,但如果使用別名,同一個 bean 可能會有多個名稱。當實現(xiàn)
九、總結
最佳實踐總結
-
啟動及配置
- 我們使用了
AnnotationConfigApplicationContext作為 Spring 容器的入口,專門為基于 Java 的配置設計。該容器被初始化并加載了MyConfiguration類,它定義了應用的主要配置。
- 我們使用了
-
組件掃描
- 通過在
MyConfiguration類中使用@ComponentScan注解,我們告訴 Spring 容器去掃描 "com.xcs.spring.service" 包及其子包,以找到和管理 Beans。
- 通過在
-
生命周期管理
-
MyBasePayService類展示了如何利用 Spring 的特殊接口,例如BeanNameAware、InitializingBean和DisposableBean,來插入到 Bean 的生命周期的特定階段。當一個 Bean 實例被創(chuàng)建并管理 by Spring, 它會被賦予一個名稱(通過BeanNameAware)、在所有屬性設置后初始化(通過InitializingBean)以及在應用結束或 Bean 被銷毀時執(zhí)行特定操作(通過DisposableBean)。
-
-
具體的服務實現(xiàn)
- 有兩個具體的支付服務,
MyAliPayService和MyWeChatPayService,它們都繼承了MyBasePayService。這意味著它們都自動繼承了上述的生命周期管理功能。當 Spring 容器啟動時,這兩個服務的相關生命周期方法會被調用,如我們從打印的消息中所看到的。
- 有兩個具體的支付服務,
-
實際效果
- 當應用運行時,每個服務類都會打印出其已經被注冊和注銷的消息,這是由于它們都繼承了
MyBasePayService中定義的生命周期方法。
- 當應用運行時,每個服務類都會打印出其已經被注冊和注銷的消息,這是由于它們都繼承了
源碼分析總結
-
啟動和上下文初始化
- 使用
AnnotationConfigApplicationContext初始化Spring容器,其中傳遞了配置類MyConfiguration。
- 使用
-
注冊和刷新上下文
- 在
AnnotationConfigApplicationContext構造函數中,register()方法注冊配置類,而refresh()方法開始加載和初始化beans。
- 在
-
開始bean的實例化
-
refresh()方法進一步調用了finishBeanFactoryInitialization(beanFactory),該方法負責預先實例化所有非懶加載的單例bean。
-
-
實例化單例bean
-
preInstantiateSingletons()方法遍歷所有bean名稱,并通過調用getBean(beanName)來實例化和初始化bean。
-
-
創(chuàng)建bean實例
-
doGetBean()是實際進行bean創(chuàng)建的核心方法,它處理了bean的實例化、依賴注入和初始化等邏輯。
-
-
處理Aware接口族
- 在bean的初始化過程中,
invokeAwareMethods(beanName, bean)被調用,負責處理實現(xiàn)了Aware接口族的beans。這是我們的BeanNameAware接口發(fā)揮作用的地方,當bean實現(xiàn)此接口時,其setBeanName方法會被調用。
- 在bean的初始化過程中,
-
用戶定義的邏輯
- 在
MyBasePayService類中,我們實現(xiàn)了BeanNameAware接口,并重寫了setBeanName方法來保存bean的名稱。此外,還使用了InitializingBean和DisposableBean接口來在bean的生命周期的特定時刻執(zhí)行自定義的邏輯。
- 在