SpringIoc容器之Aware

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/07/06/17531354.html
-Advertisement-
Play Games

Aware是Spring提供的一個標記超介面,指示bean有資格通過回調樣式的方法由Spring容器通知特定的框架對象,以獲取到容器中特有對象的實例的方法之一。實際的方法簽名由各個子介面確定,但通常只包含一個接受單個參數的void返回方法。 ...


1 前言

Aware是Spring提供的一個標記超介面,指示bean有資格通過回調樣式的方法由Spring容器通知特定的框架對象,以獲取到容器中特有對象的實例的方法之一。實際的方法簽名由各個子介面確定,但通常只包含一個接受單個參數的void返回方法。

2 Spring中9個Aware內置實現

|--Aware
    |--BeanNameAware
    |--BeanClassLoaderAware
    |--BeanFactoryAware
    |--EnvironmentAware
    |--EmbeddedValueResolverAware
    |--ResourceLoaderAware
    |--ApplicationEventPublisherAware
    |--MessageSourceAware
    |--ApplicationContextAware



9個內置實現又分兩類,前三個為直接調用,後6個通過ApplicationContextAwareProcessor後置處理器,間接回調

2.1 BeanNameAware

public interface BeanNameAware extends Aware {

       /**
        *設置創建此bean的bean工廠中的bean的名稱。
        *在普通bean屬性填充之後但在
        *初始化之前回調,如{@link InitializingBean#afterPropertiesSet()}
        *或自定義初始化方法。
        * @param name工廠中bean的名稱。
        *註意,此名稱是工廠中使用的實際bean名稱,這可能
        *與最初指定的名稱不同:特別是對於內部bean
        * names,實際的bean名稱可以通過添加
        *“#…”尾碼。使用{@link BeanFactoryUtils#originalBeanName(String)}
        *方法提取原始bean名稱(不帶尾碼),如果需要的話。
        * /
	void setBeanName(String name);

}



實現BeanNameAware介面需要實現setBeanName()方法,這個方法只是簡單的返回我們當前的beanName,這個介面錶面上的作用就是讓實現這個介面的bean知道自己在spring容器里的名字,而且官方的意思是這個介面更多的使用在spring的框架代碼中,實際開發環境應該不建議使用,因為spring認為bean的名字與bean的聯繫並不是很深,(的確,拋開spring API而言,我們如果獲取了該bean的名字,其實意義不是很大,我們沒有獲取該bean的class,只有該bean的名字,我們也無從下手,相反,因為bean的名稱在spring容器中可能是該bean的唯一標識,也就是說再beanDefinitionMap中,key值就是這個name,spring可以根據這個key值獲取該bean的所有特性)所以spring說這個不是非必要的依賴。

2.2 BeanClassLoaderAware

public interface BeanClassLoaderAware extends Aware {

   /**
    *提供bean {@link ClassLoader}類載入器的回調
    *一個bean實例在屬性的填充之後但在初始化回調之前調用
    * {@link InitializingBean
    * {@link InitializingBean#afterPropertiesSet()}
    *方法或自定義初始化方法。
    * @param類載入器擁有的類載入器;可能是{@code null}在例如,必須使用預設的{@code ClassLoader}
    * 獲取的{@code ClassLoader}
    * {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
    * /
   void setBeanClassLoader(ClassLoader classLoader);

}



在bean屬性填充之後初始化之前,提供類加制器的回調。讓受管Bean本身知道它是由哪一類裝載器負責裝載的。

2.3 BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   /**
    * 為bean實例提供所屬工廠的回調。
    * 在普通bean屬性填充之後調用但在初始化回調之前,如
    * {@link InitializingBean#afterPropertiesSet()}或自定義初始化方法。
    * @param beanFactory擁有beanFactory(非空)。bean可以立即調用工廠上的方法。
    * @在初始化錯誤時拋出BeansException
    * @參見BeanInitializationException
    * /
   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}



在bean屬性填充之後初始化之前,提bean工廠的回調。實現 BeanFactoηAware 介面的 bean 可以直接訪問 Spring 容器,被容器創建以後,它會擁有一個指向 Spring 容器的引用,可以利用該bean根據傳入參數動態獲取被spring工廠載入的bean

2.4 EnvironmentAware

public interface EnvironmentAware extends Aware {

   /**
    * 設置該對象運行的{@code環境}。
    */
   void setEnvironment(Environment environment);

}



設置該對象運行的。所有註冊到 Spring容器內的 bean,只要該bean 實現了 EnvironmentAware介面,並且進行重寫了setEnvironment方法的情況下,那麼在工程啟動時就可以獲取得 application.properties 的配置文件配置的屬性值,這樣就不用我們將魔法值寫到代碼裡面了。

2.5 EmbeddedValueResolverAware

public interface EmbeddedValueResolverAware extends Aware {

   /**
    * 設置StringValueResolver用於解析嵌入的定義值。
    */
   void setEmbeddedValueResolver(StringValueResolver resolver);

}



在基於Spring獲取properties文件屬性值的時候,一般使用@Value的方式註入配置文件屬性值,但是@Value必須要在Spring的Bean生命周期管理下才能使用,比如類被@Controller、@Service、@Component等註解標註。如有的抽象類中,基於Spring解析@Value的方式,使用EmbeddedValueResolverAware解析配置文件來實現。

2.6 ResourceLoaderAware

public interface ResourceLoaderAware extends Aware {

   /**
    *設置該對象運行的ResourceLoader。這可能是一個ResourcePatternResolver,它可以被檢查
    *通過{@code instanceof ResourcePatternResolver}。另請參閱
    * {@code ResourcePatternUtils。getResourcePatternResolver}方法。
    * <p>在填充普通bean屬性之後但在init回調之前調用
    *像InitializingBean的{@code afterPropertiesSet}或自定義初始化方法。
    *在ApplicationContextAware的{@code setApplicationContext}之前調用。
    * @param resourceLoader該對象使用的resourceLoader對象
    * @ @ springframework.core. io.support.resourcepatternresolver
    * @ @ resourcepatternutils #獲取resourcepatternresolver
    * /
   void setResourceLoader(ResourceLoader resourceLoader);

}



ResourceLoaderAware 是特殊的標記介面,它希望擁有一個 ResourceLoader 引用的對象。當實現了 ResourceLoaderAware介面的類部署到application context(比如受Spring管理的bean)中時,它會被application context識別為 ResourceLoaderAware。 接著application context會調用setResourceLoader(ResourceLoader)方法,並把自身作為參數傳入該方法(記住,所有Spring里的application context都實現了ResourceLoader介面)。

既然 ApplicationContext 就是ResourceLoader,那麼該bean就可以實現 ApplicationContextAware介面並直接使用所提供的application context來載入資源,但是通常更適合使用特定的滿足所有需要的 ResourceLoader 實現。 這樣一來,代碼只需要依賴於可以看作輔助介面的資源載入介面,而不用依賴於整個Spring ApplicationContext 介面。

2.7 ApplicationEventPublisherAware

public interface ApplicationEventPublisherAware extends Aware {

   /**
    *設置該對象運行的ApplicationEventPublisher。
    * <p>在普通bean屬性填充之後但在init之前調用像InitializingBean的afterPropertiesSet或自定義初始化方法。
    *在ApplicationContextAware的setApplicationContext之前調用。
    *該對象使用的事件發佈者
    * /
   void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

}



ApplicationEventPublisherAware 是由 Spring 提供的用於為 Service 註入 ApplicationEventPublisher 事件發佈器的介面,使用這個介面,我們自己的 Service 就擁有了發佈事件的能力。

2.8 MessageSourceAware

public interface MessageSourceAware extends Aware {

   /**
    *設置該對象運行的MessageSource。
    * <p>在普通bean屬性填充之後但在init之前調用像InitializingBean的afterPropertiesSet或自定義初始化方法。
    *在ApplicationContextAware的setApplicationContext之前調用。
    * @param messageSource消息源
    * /
   void setMessageSource(MessageSource messageSource);

}



獲得message source這樣可以獲得文本信息,使用場景如為了國際化。

2.9 ApplicationContextAware

public interface ApplicationContextAware extends Aware {

   /**
    *設置該對象運行的ApplicationContext。通常這個調用將用於初始化對象。
    * <p>在普通bean屬性填充之後但在init回調之前調用
    *作為{@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
    *或自定義初始化方法。在{@link ResourceLoaderAware#setResourceLoader}之後調用,
    * {@link ApplicationEventPublisherAware#setApplicationEventPublisher}和
    * {@link MessageSourceAware},如果適用。
    * @param applicationContext該對象將使用的applicationContext對象
    * @在上下文初始化錯誤時拋出ApplicationContextException如果由應用程式上下文方法拋出,則拋出BeansException
    * @see org.springframework.beans.factory.BeanInitializationException
    * /
   void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}



ApplicationContextAware的作用是可以方便獲取Spring容器ApplicationContext,從而可以獲取容器內的Bean。ApplicationContextAware介面只有一個方法,如果實現了這個方法,那麼Spring創建這個實現類的時候就會自動執行這個方法,把ApplicationContext註入到這個類中,也就是說,spring 在啟動的時候就需要實例化這個 class(如果是懶載入就是你需要用到的時候實例化),在實例化這個 class 的時候,發現它包含這個 ApplicationContextAware 介面的話,sping 就會調用這個對象的 setApplicationContext 方法,把 applicationContext Set 進去了。

3 Spring中調用時機

Aware介面由Spring在AbstractAutowireCapableBeanFactory.initializeBean(beanName, bean,mbd)方法中通過調用invokeAwareMethods(beanName, bean)方法和applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)觸發Aware方法的調用

image.png

3.1 invokeAwareMethods

private void invokeAwareMethods(final String beanName, final Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof BeanNameAware) {
         ((BeanNameAware) bean).setBeanName(beanName);
      }
      if (bean instanceof BeanClassLoaderAware) {
         ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
      }
      if (bean instanceof BeanFactoryAware) {
         ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
   }
}



判斷並直接回調

3.2 applyBeanPostProcessorsBeforeInitialization

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      result = beanProcessor.postProcessBeforeInitialization(result, beanName);
      if (result == null) {
         return result;
      }
   }
   return result;
}



通過ApplicationContextAwareProcessor.postProcessBeforeInitialization(Object bean, String beanName)間接調用,併在方法invokeAwareInterfaces中進行回調。

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
   AccessControlContext acc = null;

   if (System.getSecurityManager() != null &&
         (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
               bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
               bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
      acc = this.applicationContext.getBeanFactory().getAccessControlContext();
   }

   if (acc != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            invokeAwareInterfaces(bean);
            return null;
         }
      }, acc);
   }
   else {
      invokeAwareInterfaces(bean);
   }

   return bean;
}

private void invokeAwareInterfaces(Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof EnvironmentAware) {
         ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
      }
      if (bean instanceof EmbeddedValueResolverAware) {
         ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
               new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
      }
      if (bean instanceof ResourceLoaderAware) {
         ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
      }
      if (bean instanceof ApplicationEventPublisherAware) {
         ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
      }
      if (bean instanceof MessageSourceAware) {
         ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
      }
      if (bean instanceof ApplicationContextAware) {
         ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
      }
   }
}



4 總結

通過上面的分析,可以知道Spring生命周期中的初始化方法里,在真正執行初始化方法之前,分別通過invokeAwareMethods方法和後置處理器ApplicationContextAwareProcessor來觸發Aware的調用,那麼,Spring為什麼要使用兩種方式而不使用其中之一呢?

通過本章我們瞭解了9中內置介面的作用,以及它們能夠獲取到的不同上下文信息。

作者:京東零售 曾登均

來源:京東雲開發者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • **startswith()方法** startswith() 方法用於檢索字元串是否以指定字元串開頭,如果是返回 True;反之返回 False。 **endswith()方法** endswith() 方法用於檢索字元串是否以指定字元串結尾,如果是則返回 True;反之則返回 False ``` ...
  • ### 1.os.system() os.system() 是對 C 語言中 system() 系統函數的封裝,允許執行一條命令,並返回退出碼(exit code),命令輸出的內容會直接列印到屏幕上,無法直接獲取。 示例: ```python # test.py import os os.syste ...
  • 哈嘍大家好,我是鹹魚 當我們說 Python 時,通常指的是官方實現的 CPython 但還有很多比如 Pypy、Jython、MicroPython、Brython、RustPython 等 “python” 許多小伙伴看到這些帶 “python” 的概念可能一頭霧水,心想這跟我平時接觸到的 py ...
  • # ServletContext對象 **每一個 web 應用都有且僅有一個 ServletContext 對象**,又稱為 Application 對象,從名稱中可知,該對象是與應用程式相關的。在WEB 容器啟動時,會為每一個 WEB 應用創建一個對應的 ServletContex對象。 **該對 ...
  • 當初剛開始學單鏈表學的是一頭霧水,簡直就是徹頭徹尾災難,一塌糊塗,過段時間後經過自己的重新認真思考再結合小練習明白了它是怎麼個回事兒。 1、首先從它的邏輯上入手,對他有大體認知。 簡單來說就是一個一個有方向小塊兒連在一起,好像疫情期間大家排隊做核酸,都朝著醫護人員那個方向,醫護人員會從第一個開始數有 ...
  • 目前為止,介紹的`numpy`數組基本都是關於數值的,其實,`numpy`本身就是一個用於數值計算的基礎庫。 不過,除了數值計算之外,`numpy`也能夠支持**結構化數組**。 # 1. 關聯不同類型數據 `numpy`的數組為了提高計算性能,要求數組的數據類型要一致。但是現實情況下,我們經常遇到 ...
  • **爬蟲,這個經常被人提到的詞,是對數據收集過程的一種形象化描述。特別是在Python語言中,由於其豐富的庫資源和良好的易用性,使得其成為編寫爬蟲的絕佳選擇。本文將從基礎知識開始,深入淺出地講解Python爬蟲的相關知識,並分享一些獨特的用法和實用技巧。本文將以實際的網站為例,深入闡述各個處理部分, ...
  • # 劇透警告,沒寫過的勿觸 題目: > 編寫一個方法,找出兩個數字a和b中最大的那一個。不得使用if-else或其他比較運算符。 qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq qwq q ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...