BeanNameAware源碼分析

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);

}

四、主要功能

  1. 提供 setBeanName 方法

    • 當一個 Bean 實現(xiàn)了 BeanNameAware 接口,它需要提供 setBeanName 方法的實現(xiàn)。這個方法有一個參數,即該 Bean 在 Spring 容器中的名稱。
  2. 自動回調

    • 當 Spring 容器創(chuàng)建并配置一個實現(xiàn)了 BeanNameAware 接口的 Bean 時,容器會自動回調 setBeanName 方法,并傳入該 Bean 在容器中的名稱。這意味著我們不需要顯式地調用這個方法;Spring 容器會自動處理。
  3. 獲取 Bean 的名稱

    • 有時,Bean 可能需要知道其在容器中的名稱以執(zhí)行特定的邏輯或功能,或者為了日志記錄或其他目的。通過實現(xiàn) BeanNameAware,Bean 可以輕松獲得此信息。

五、最佳實踐

首先來看看啟動類入口,上下文環(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.");
    }
}

MyAliPayServiceMyWeChatPayService 是兩個支付服務類,都繼承自 MyBasePayService,因此它們會自動獲得與 Spring 容器生命周期相關的基本功能。這種設計方式為多個支付服務提供了一個共同的生命周期管理模式,同時允許每個服務添加其特定的支付邏輯。

@Service
public class MyAliPayService extends MyBasePayService{
    
}

@Service
public class MyWeChatPayService extends MyBasePayService{
    
}

運行結果發(fā)現(xiàn),當 Spring 容器啟動并初始化 Beans 時,它正確地識別并實例化了 MyAliPayServiceMyWeChatPayService 這兩個服務。由于這兩個服務都繼承自 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、BeanClassLoaderAwareBeanFactoryAware 等接口時,此方法確保正確的回調方法被調用,從而為 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.");
    }
}

八、注意事項

  1. 與其他生命周期方法的順序

    • setBeanName 方法的調用是在其他許多生命周期方法之前的,例如 InitializingBean#afterPropertiesSet 和任何定義的初始化方法。因此,我們不應該在 setBeanName 方法內部預期其他配置或初始化邏輯已經完成。
  2. 僅在容器管理的 Beans 中有效

    • 只有當 bean 是由 Spring 容器管理時,BeanNameAware 才會生效。簡單地創(chuàng)建一個類的實例(例如通過 new 關鍵字)并不會觸發(fā) BeanNameAware 功能。
  3. 與其他 Aware 接口的組合使用

    • 當一個 bean 同時實現(xiàn)多個 Aware 接口時,需要注意它們的調用順序。例如,BeanNameAware、BeanFactoryAwareApplicationContextAware 的回調方法調用順序是固定的。
  4. Bean 名稱的唯一性

    • Spring 容器內的 bean 名稱是唯一的,但如果使用別名,同一個 bean 可能會有多個名稱。當實現(xiàn) BeanNameAware 時,我們獲得的是 bean 的主要名稱。

九、總結

最佳實踐總結

  1. 啟動及配置

    • 我們使用了 AnnotationConfigApplicationContext 作為 Spring 容器的入口,專門為基于 Java 的配置設計。該容器被初始化并加載了 MyConfiguration 類,它定義了應用的主要配置。
  2. 組件掃描

    • 通過在 MyConfiguration 類中使用 @ComponentScan 注解,我們告訴 Spring 容器去掃描 "com.xcs.spring.service" 包及其子包,以找到和管理 Beans。
  3. 生命周期管理

    • MyBasePayService類展示了如何利用 Spring 的特殊接口,例如 BeanNameAware、InitializingBeanDisposableBean,來插入到 Bean 的生命周期的特定階段。當一個 Bean 實例被創(chuàng)建并管理 by Spring, 它會被賦予一個名稱(通過 BeanNameAware)、在所有屬性設置后初始化(通過 InitializingBean)以及在應用結束或 Bean 被銷毀時執(zhí)行特定操作(通過 DisposableBean)。
  4. 具體的服務實現(xiàn)

    • 有兩個具體的支付服務,MyAliPayServiceMyWeChatPayService,它們都繼承了 MyBasePayService。這意味著它們都自動繼承了上述的生命周期管理功能。當 Spring 容器啟動時,這兩個服務的相關生命周期方法會被調用,如我們從打印的消息中所看到的。
  5. 實際效果

    • 當應用運行時,每個服務類都會打印出其已經被注冊和注銷的消息,這是由于它們都繼承了 MyBasePayService 中定義的生命周期方法。

源碼分析總結

  1. 啟動和上下文初始化

    • 使用AnnotationConfigApplicationContext初始化Spring容器,其中傳遞了配置類MyConfiguration
  2. 注冊和刷新上下文

    • AnnotationConfigApplicationContext構造函數中,register()方法注冊配置類,而refresh()方法開始加載和初始化beans。
  3. 開始bean的實例化

    • refresh()方法進一步調用了finishBeanFactoryInitialization(beanFactory),該方法負責預先實例化所有非懶加載的單例bean。
  4. 實例化單例bean

    • preInstantiateSingletons()方法遍歷所有bean名稱,并通過調用getBean(beanName)來實例化和初始化bean。
  5. 創(chuàng)建bean實例

    • doGetBean()是實際進行bean創(chuàng)建的核心方法,它處理了bean的實例化、依賴注入和初始化等邏輯。
  6. 處理Aware接口族

    • 在bean的初始化過程中,invokeAwareMethods(beanName, bean)被調用,負責處理實現(xiàn)了Aware接口族的beans。這是我們的BeanNameAware接口發(fā)揮作用的地方,當bean實現(xiàn)此接口時,其setBeanName方法會被調用。
  7. 用戶定義的邏輯

    • MyBasePayService類中,我們實現(xiàn)了BeanNameAware接口,并重寫了setBeanName方法來保存bean的名稱。此外,還使用了InitializingBeanDisposableBean接口來在bean的生命周期的特定時刻執(zhí)行自定義的邏輯。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容