當前位置:首頁 » 安卓系統 » 安卓如何判斷組件有循環依賴

安卓如何判斷組件有循環依賴

發布時間: 2023-05-14 05:47:36

❶ 安卓android的components指什麼,謝謝

安卓Android平台指紋性格測試是一款依據指紋測試性格的應用,因為世界上絕對找不到兩個指畢團紋完全相同的人,所以「指紋薯純」就被當做犯罪偵查上的重要線索手手橘之一。雖

❷ 關於安卓手機桌面上面的小組件怎麼添加和修改。。

1、打開手機界面

❸ 安卓開發線程和進程講解


本教程為大家介紹安卓開發中的線程和進程,安卓平台中當首次啟動運行一個組件的時候,Android會相應的啟動了一個進程。默認的,所有的組件和程序運行在這個進程和線程中,也可以安排組件在其他的進程或者線程中運行。
進程:組件運行的進程由manifest file控制。組件的節點activity, service, receiver, 和 provider 都包含一個 process 屬性。這個屬性可以設置組件運行的進程:可以配置組件在一個獨立進程運行,或者多個組件在同一個進程運行。甚至可以多個程序在一個進程中運行——如果這些程序共享一個User ID並給定同樣的許可權。 節點也包含 process 屬性,用來設置程序中所有組件的默認進程。
所有的組件在此進程的主線程中實例化,系統對這些組件的調用從主線程中分離。並非每個對象都會從主線程中分離。一般來說,響應例如View.onKeyDown()用戶操作的方法和通知的方法也在主線程中運行。這就表示,組件被系統調用的時候不應該長時間運行或者阻塞操作(如網路操作或者計算大量數據),因為這樣會阻塞進程中的其他組件。可以把這類操作從主線程中分離。
當更加常用的進程無法獲取足夠內存,Android可能會關閉不常用的進程。下次啟動程序的時候會重新啟動進程。
當決定哪個進程需要被關閉的時候, Android會考慮哪個對用戶更加有用。如Android會傾向於關閉一個長期不顯示在界面的旦頌殲進程來支持一個經常顯示在界面的進程。
線程:即使為組件分配了不同的進程,有時候也需要再分配線程。比如用戶界面需要很快對用戶進行響應,因此某些費時的操作,如網路連接、下載或者非常佔用伺服器時間的操作應該放到其他線程。
線程通過java的標准對象Thread 創建. Android 提供了很多方便的管理線程的方法:— Looper 在線程中運行一個消息循環; Handler 傳遞一個消息; HandlerThread 創建一個帶有消息循環的線程。
遠程調用Remote procere calls
Android有一個遠程調用(RPCs) 的輕量級機制— 通過這個機制,方法可以在本地調用,在遠程執行(在其他進程執行),還可以返回一個值。要實現這個需求,必須分解方法調用,並且所有要傳遞的數據必須是操作系統可以訪問的級別。從本地的進程和內存地址傳送到遠程的進程和內存地櫻正址並在遠程處理和返回。返回值必須向相反的方向傳遞。Android提供了做以上操作的代碼,所以開發者可以專注於實現RPC的介面。
一個RPC介面只能包含方法。所有的方法都是同步執行的(直到遠程方法返回,本地方法才結束阻塞),沒有返回值的時候也是如此。
簡單來說,這個機制是這樣的:使用IDL (interface definition language)定義你想要實現的介面, aidl 工具可以生成用於java的介面定義,本地和遠程都要使用這個定義。它包含2個類,
inner類包含了所有的管理遠程程序(符合IDL描述的介面)所需要的代碼。所有的inner類實現了IBinder 介面.其中一個在本地使用,可以不管它的代碼;另外一個叫做Stub繼承了 Binder 類。為了實現遠程調用,這個類包含RPC介面。開發者可以繼承Stub類來實現需要的方法。
一般來說,遠程進程會被一個service管理(因為service可以通知操作系統這個進程的信息並和其他進程通信),它也會包含aidl 工具產生的介面文件,Stub類實現了遠處那個方法。服務的客戶端只需要aidl 工具產生的介面文件。
以下是如何連接服務和客戶端調用:
·服務的客戶端(本地)會實現onServiceConnected() 和onServiceDisconnected() 方法,這樣,當客戶端連接或者斷開連接的時候可以獲取到通知。通過 bindService() 獲取到服務的連接。
· 服務的 onBind() 方法中可以接收或者拒絕連接,取決它收到的intent (intent通過 bindService()方法連接到服務). 如果服務接收了連接,會返回一個Stub類的實例.
· 如果服務接受了連接,Android會調用客戶端的onServiceConnected() 方法,模沖並傳遞一個Ibinder對象(系統管理的Stub類的代理),通過這個代理,客戶端可以連接遠程的服務。
以上的描述省略很多RPC的機制。請參見Designing a Remote Interface Using AIDL 和 IBinder 類。
線程安全的方法
在某些情況下,方法可能調用不止一個的線程,因此需要注意方法的線程安全。
對於可以遠程調用的方法,也要注意這點。當一個調用在Ibinder對象中的方法的程序啟動了和Ibinder對象相同的進程,方法就在Ibinder的進程中執行。但是,如果調用者發起另外一個進程,方法在另外一個線程中運行,這個線程在和IBinder對象在一個線程池中;它不會在進程的主線程中運行。例如,一個service從主線程被調用onBind() 方法,onBind() 返回的對象(如實現了RPC的Stub子類)中的方法會被從線程池中調用。因為一個服務可能有多個客戶端請求,不止一個線程池會在同一時間調用IBinder的方法。因此IBinder必須線程安全。
簡單來說,這個機制是這樣的:使用IDL (interface definition language)定義你想要實現的介面, aidl 工具可以生成用於java的介面定義,本地和遠程都要使用這個定義。它包含2個類,
inner類包含了所有的管理遠程程序(符合IDL描述的介面)所需要的代碼。所有的inner類實現了IBinder 介面.其中一個在本地使用,可以不管它的代碼;另外一個叫做Stub繼承了 Binder 類。為了實現遠程調用,這個類包含RPC介面。開發者可以繼承Stub類來實現需要的方法。
一般來說,遠程進程會被一個service管理(因為service可以通知操作系統這個進程的信息並和其他進程通信),它也會包含aidl 工具產生的介面文件,Stub類實現了遠處那個方法。服務的客戶端只需要aidl 工具產生的介面文件。
以下是如何連接服務和客戶端調用:
·服務的客戶端(本地)會實現onServiceConnected() 和onServiceDisconnected() 方法,這樣,當客戶端連接或者斷開連接的時候可以獲取到通知。通過 bindService() 獲取到服務的連接。
· 服務的 onBind() 方法中可以接收或者拒絕連接,取決它收到的intent (intent通過 bindService()方法連接到服務). 如果服務接收了連接,會返回一個Stub類的實例.
· 如果服務接受了連接,Android會調用客戶端的onServiceConnected() 方法,並傳遞一個Ibinder對象(系統管理的Stub類的代理),通過這個代理,客戶端可以連接遠程的服務。
線程安全的方法
在某些情況下,方法可能調用不止一個的線程,因此需要注意方法的線程安全。
對於可以遠程調用的方法,也要注意這點。當一個調用在Ibinder對象中的方法的程序啟動了和Ibinder對象相同的進程,方法就在Ibinder的進程中執行。但是,如果調用者發起另外一個進程,方法在另外一個線程中運行,這個線程在和IBinder對象在一個線程池中;它不會在進程的主線程中運行。例如,一個service從主線程被調用onBind() 方法,onBind() 返回的對象(如實現了RPC的Stub子類)中的方法會被從線程池中調用。因為一個服務可能有多個客戶端請求,不止一個線程池會在同一時間調用IBinder的方法。因此IBinder必須線程安全。
簡單來說,一個content provider 可以接收其他進程的數據請求。即使ContentResolver和ContentProvider類沒有隱藏了管理交互的細節,ContentProvider中響應這些請求的方法(query(), insert(), delete(), update(), and getType() )— 是在content provider的線程池中被調用的,而不是ContentProvider的本身進程。因為這些方法可能是同時從很多線程池運行的,所以這些方法必須要線程安全。

❹ 如何組織應用程序的結構

適用於應用程序使用的軟體設計和構架總結軟體架構一般定義為應用程序的結構。在定義這些結構的時候,軟體架構師的目標就是使用不同級別的抽象,通過根據關注點把功能進行分割來最小化復雜度。我們會從最高層的抽象以及不同的關注點開始研究。因為設計的過程中需要不斷深入這些層次、擴展關注點直到定義了結構為止。內容目標概覽概要步驟第一步——選擇我們的分層策略第二步——定義層之間的介面第三步——選擇我們的部署策略第四步——選擇通訊協議其它資源目標找出並選擇分層策略定義層之間的介面找出並選擇部署策略選擇合適的通訊協議概覽在開始應用程序設計的時候,我們第一個任務就是關注最高層次的抽象並且開始把功能分組到不同的層中。然後,我們需要根據我們所設計的應用程序的類型為每一層定義公共的介面。在定義了層和介面之後我們就需要決定應用程序是如何部署的。最後,最後一個步驟包含選擇會用於在應用程序不同邏輯層和物理侍巧枯層之間通訊的協議。概要步驟第一步——選擇我們的分層策略第二步——定義層之間的介面第三步——選擇我們的部署策略第四步——選擇通訊協議第一步——選擇我們的分層策略層表示組件按照不同關注點的邏輯分組。例如,業務邏輯表示應該分組到某層的一個關寬族注點。這可能是應用程序最初設計中最重要的一個抉擇。然而,有很多種方式可以把功能進行分層。在完成了本步驟之後你應該能理解如何來組織應用程序的分層結構。下圖演示了用於大多數業務應用程序設計的主要層。在這個示例中,功能按照表現邏輯、服務邏輯、業務邏輯和數據訪問邏輯進行分組。如果服務沒有公開的話,就不需要服務層,那麼我們應該只有表現、業務和數據訪問層。 這只是一個示例,不是對應用程序進行分層的唯一方式。然而,一般來說分層在軟體設計中很常見。底層的操作系統設計使用了RING的方式,RING0(內核)表示最低的層,它可以直接訪問諸如CPU和內存等硬體資源。設備驅動作為外層RING通過RING0提供的介面和硬體進行交互。應用程序代碼包含在最外部的RING中,它們通過設備驅動來和硬體交互。除了層,我們還有一些功能會跨越層,它們通常稱作橫切。此類功能包含諸如日誌以及異常管理等。有不同的方式來處理這種功能,比如公共類庫、使用元數據直接在編譯輸出中插入橫切代碼的面向方面編程(AOP)等。在為應用程序選擇分層策略的時候,如下事項是我們需要考慮的關鍵點:決定我們是否需要層選擇我們需要的層決定我們是否需要合並層決定層之間交互的規則決定我們是否需要層在大多數情況下總是需要把功能進行分層。然而,在有些特例下不需要。例如,如果我們構建一個非常小的應用程序,可能就會考慮在用戶界面事件處理程序中實現表現、業務邏輯以及數據訪問功能。此外,一些軟體工程師會認為跨層邊界或增加層帶來的開銷會對性能有負面影響。 是否使用分層的決定說白了就是性能對可維護性。雖然不使用分層可以提升性能,因為所有的東西都緊密耦合在了一起。但是,這種耦合會嚴重影響應用程序的擴展和維護能力。說實話,對可擴展性和可維護性的影響遠比獲得的那一點性能的提升重大。不管怎麼始終應該考慮分層,我們的目標其實是決定應用程序需要怎麼樣合適的分層。選擇我們需要的層有幾種不同的方式來把功能進行分層。例如一種分層的方式就是表現、服務和數據訪問。許多關注業務領域的面向對象設計使用又一種不同的分層策略,最底層用於基礎結構代碼,提供類似數據訪問的支持。上面一層就是領域層,用於包含業務領域對象,最頂層包含應用程序代碼。對於使用微軟.NET Framework開發的大多數業務應用程序來說,按照表現、服務、業務以及數據訪問對功能進行分組是不錯的選擇。下面描述了每一層中的一些組件,以及什麼時候不需要這層。表現-這層包含用於處理用戶介面請求以及呈現用戶介面輸出的組件。如果我們的應用程序沒有用戶介面的話就不需要表現層。服務-這層包含用於處理服務請求的組件。例如,對於使用Windows Communication Foundation (WCF)的應用程序,我們就會找到定義契約的組件、用於實現介面以及提供在實現類和外部契約類之間提供轉換支持的組件。如果我們的應用程序不提供服老洞務,則不需要在設計中包含服務層。Business業務-這層包含用於處理表現或服務層請求的組件。在這層中的組件包括業務處理、業務實體以及業務工作流。在一些情況下,我們不需要有任何的業務層並且只需要從數據源獲取數據,也就是說這種情況下不需要業務層。數據訪問-這層包含用於管理和數據源交互的組件。例如,使用ADO.NET的話在數據訪問層中就會有連接和命令組件。如果我們的應用程序不使用外部數據的話那麼我們就不需要實現數據訪問層。決定我們是否需要合並層例如,應用程序具有很有限的業務規則並且主要關注驗證的話可以在一層中實現業務和表現邏輯。在以下情況考慮合並層: 如果我們的業務規則很有限,在一層中實現業務和表現邏輯是合理的。如果我們的應用程序從服務獲取數據並且顯示數據,可以直接為表現層添加服務引用並且直接使用服務數據。這樣的話就可以合並數據訪問和表現。 如果數據訪問服務直接訪問資料庫中的表或視圖的話可以考慮在服務層中實現數據訪問功能。還有一些適用於合並分層的示例。然而,基本准則是我們總是應該把功能分層。在一些情況下,層只不過是一層調用並且沒有提供什麼功能。然而,如果把功能進行分離的話我們就可以在影響很小或沒有影響的情況下擴展其功能。決定層之間的交互規則分層策略帶來的一個問題是我們需要定義層之間如何交互的規則。指定這樣的一種交互規則的主要原因是最小化依賴以及消除循環引用。例如,如果兩個層依賴另外那個層的組件就很可能帶來循環依賴。最常見的准則是只允許層之間的單向交互。從上到下的交互–比較高的層可以和其下層進行交互,但是低層不可以和其上的層進行交互。在設計中總是應該強制這樣的准則來避免層之間的循環依賴。緊密交互 –每一層只能直接和其下的層進行交互。這個准則可以強制嚴格的關注分離,應該每一層只知道其直接下屬層。這個准則的好處是對某一層的修改只會影響其直接上層。鬆散交互–高層可以跳過一些層直接和底層進行交互。這可以增加性能,但是也會增加依賴。換句話說,對底層的修改可能會影響其上的多個層。考慮使用緊密交互規則:如果我們設計的應用程序會不斷修改來增加新的功能,並且我們希望最小化改變的影響。如果我們設計的應用程序可能會跨物理層進行分布。考慮使用鬆散交互規則:如果我們設計的應用程序肯定不會跨物理層進行分布。例如是安裝在客戶機上的獨立富客戶端應用程序。如果我們設計一個很小的應用程序,影響多層的改動工作量不大。檢查點在結束本步驟之前,你應該可以回答如下問題:你是否找到了應用程序需要的功能並且把功能分層?你是否定義了層之間如何交互的規則?第二步——定義層之間的介面對於為層定義介面,最主要的原則是在層之間使用鬆散耦合。意思就是層不應該公開內部組件讓其它層來依賴。而是,層的介面應該設計為最小的依賴性,提供公共介面並且隱藏層中組件的細節。隱藏細節叫做抽象,有很多種方式實現抽象。如下設計方式用來為層定義介面:抽象介面 –可以通過定義抽象基類或類型定義來實現。基類或類型定義了所有層消費者用於和層進行交互的公共介面。通過使用實現抽象介面的測試對象,有的時候又叫做mock對象,可以增進可測試性。公共設計類型 –許多設計模式定義表示不同層中介面的對象類型。這些對象類型提供了一個抽象,它隱藏了層中的細節。例如,表數據入口模式定義了表示資料庫中表的對象類型。這些對象負責實現和資料庫交互的必要SQL代碼。對象的消費者不需要知道使用的SQL甚至不需要知道連接資料庫和執行命令的細節。依賴倒置 –這是一種編程模式,抽象介面定義在任何層的外部或不依賴於任何層。層不依賴於公共介面,而不是一個層依賴於另一個層。依賴注入模式是依賴導致的常見實現方式。通過依賴注入組件,一層可以通過使用公共抽象介面來注入到其它層的組件中。基於消息 –基於消息的介面可以用於提供層之間的交互,而不是直接和組件進行交互。有多種消息解決方案,比如web服務和支持跨機器和進程邊界的微軟消息隊列。然而,你也可以組合抽象介面和用於定義要交互的數據結構的公共消息類型。 基於消息最主要的區別是層之間的交互使用公共結構,它封裝了交互的細節。這個結構可以定義操作、數據架構、錯誤契約、安全信息以及其它和層之間通訊相關的細節。考慮使用抽象介面:如果你希望使用介面具體實例來實現不同行為的能力。如果你希望使用mock對象來增加應用程序的可測試性。考慮使用公共設計類型:如果你在為層中的介面實現設計模式。許多設計模式基於抽象介面,然而,一些基於具體類。如果你希望一種快捷而簡單的方式實現層介面。比如表數據入口模式。考慮使用依賴導致:如果你希望提供最大的可測試性,這個方式允許你諸如具體的測試類到設計中的不用層中。考慮使用基於消息的:如果你要實現一個web應用程序並且在表現層和業務層中定義介面。如果你有一個應用程序層需要支持多個客戶端類型。如果你希望提供跨物理和進程邊界交互。如果你希望以公共介面進行交互。如果你要和一個無狀態的介面進行交互,狀態信息使用消息進行攜帶。 對於web應用程序表現層和業務邏輯層之間的交互推薦使用基於消息的介面。一般通過使用Windows Communication Foundation (WCF)或提供公共消息類型的抽象介面來實現。在表現層和業務層之間使用基於消息的介面的原因是因為web應用程序的業務層不維護調用間的狀態。換句話說,每一次表現層和業務層中的調用都代表一個新的上下文。通過使用基於消息的介面我們可以隨請求傳遞上下文信息並且為表現層中的異常和錯誤處理提供一種公共的模型。檢查點在結束本步驟之前,你應該可以回答如下問題:你是否需要使用抽象介面提供測試性?你的介面是否需要提供跨進程和機器邊界交互?你的業務層是否需要支持多個客戶端類型?你是否會使用基於消息的介面在web應用程序的表現層和業務層之間進行交互?第三步——選擇部署策略對於大多數解決方案中都可以找到幾種常見的模式,它們表示了應用程序的部署結構。如果要為我們的應用程序決定最佳部署解決方案,首先需要了解常見的模式。在理解了不同的模式之後,然後你就可以考慮場景、需求以及安全約束來選擇某一種或多種模式。客戶端-伺服器這個模式表示具有兩個主要組件:客戶端和伺服器的基本結構。對於這種情況,客戶端和伺服器可以在相同機器上也可以跨越兩個機器。如下示例演示了常見的web應用程序場景,客戶端和web伺服器進行交互。N層這個模式表示應用程序組件跨域一個或多個伺服器的結構。下圖表示了2層部署,所有應用程序代碼位於客戶端而資料庫位於分離的伺服器中。3層在3層設計中,客戶端和部署在獨立伺服器的應用程序軟體進行交互,和資料庫進行交互的應用程序伺服器也是單獨的伺服器。這是許多web應用程序和web服務常見的模式。 4層對於這種情況,web伺服器從物理上和應用伺服器分離。這么做通常處於安全的目的,web伺服器部署在隔離區域(DMZ)中而應用程序伺服器位於不同的子網。我們一般會在客戶端和web層以及web層和應用層或業務邏輯層之間假設2道防火牆。考慮客戶端伺服器或2層: 如果我們在開發一個需要訪問應用伺服器的客戶端。 如果我們在開發一個訪問外部資料庫的獨立客戶端。考慮3層: 如果我們在開發基於區域網的應用程序,所有伺服器都在一個私有網路中。 如果我們在開發基於Internet的應用程序,並且安全需求不約束在公共的web/應用伺服器中實現業務邏輯。考慮4層: 如果安全需求規定業務邏輯不能在DMZ中部署業務邏輯。 如果我們的應用程序使用伺服器上的資源很厲害並且我們希望把功能分離到另一個伺服器。在大多數情況下推薦讓所有的應用程序代碼放在相同伺服器。任何需要跨物理邊界的需求都會影響性能因為數據需要在這些邊界進行序列化。然而,有一些情況下我們需要跨伺服器分離功能。此外,根據伺服器的位置我們可以選擇性能最優化的通訊協議。檢查點在結束本步驟之前,你應該可以回答如下問題: 安全需求是否規定業務邏輯不能部署在公開的DMZ中? 我們是否在開發獨立的只需要訪問資料庫的客戶端? 我們是否在開發web應用程序或web服務? 我們是否在開發有多個客戶端的應用程序? 第四步——選擇通訊協議說到設計中用於跨邏輯層或物理層進行通訊的物理協議,通訊協議的選擇對我們應用程序的性能起巨大作用。選擇通訊協議的一個主要的地方就是使用服務和資料庫進行交互。 Windows Communication Foundation (WCF) –直接支持四種協議: HTTP –用於在Internet上公開的服務。 TCP –用於在網路內部署的服務。 NamedPipes –用於服務和其使用者部署在同一伺服器的服務。 MessageQueue –用於通過微軟消息隊列實現進行訪問的服務。 Database –支持和WCF一樣的許多協議。 HTTP –用於在Internet上公開的資料庫。 TCP –用於在網路內部署的資料庫。

❺ 安卓項目 如何讓一個mole依賴於另一個mole

androidStudio(後面簡稱AS)在導入一個外部的本地mole時,AS採用的是復制一份到當前項目目錄下這種策略。這種方式在開發一個項目時並不會有什麼大問題,不過,在多個項目都引用同一個或幾個mole時就會出現大問題,這種公用的mole通常都是框架類或工具類mole,如果每個項目都復制一份mole到自己項目目錄下,如果某天發現mole中有幾個bug需要修改,你就會發現,你不得不去每個目錄下修改mole,如果是修改的東西比較多的話,絕對是欲哭無淚。

上述就是研究多項目依賴同一個mole這個問題的起因,下面直接說說怎麼解決(目前官方似乎沒有直接支持的方法,不過我相信以後肯定會支持),首先為了方便維護,我們新建一個項目,這個項目下只有公用的mole,不放任何application

❻ 使用abp框架如何避免循環依賴

使用abp框架避免循環依賴胡鄭衫首先定義兩個相互通過構造器注入依賴的bean,如下。
1、最有效、最直接的辦法就是重新設計,將組件的結構設計褲腔的更明確,避免循環依賴。
2、使用set注入只有構造方法是在上下文載入時就要求被注入,容易出現循環依賴,所以我們可以使用其他注入方式進行依賴注入,set注入與構造注入叢基不同。
3、使用Lazy,通過延遲載入的方式防止循環依賴,也就是它沒有完全的初始化,而是創建一個代理將它注入另一個,只有第一次使用它時才會創建。

❼ IOC之getBean()以及循環依賴

眾所周知,getBean是spring中實際獲取Bean實例的方法,那麼,getBean除了我們人為拿到beanFactory然後執行getBean()邏輯之外,還有哪裡會調用呢?

一個比較重要的入口就是refresh方法,spring容器創建後,會調用refresh()方法來刷新上下文,而其中的就會通過getBean()來創建非延遲載入的bean實例。

preInstantiateSingletons中就是做一些是否需要通過getBean()提前獲取bean的邏輯判斷:

比較重要的getBean()的入口就是這里了,下鍵培面來看下 getBean()中做了什麼。

getBean()中直接調用了doGetBean方法,這是spring 的一貫風格,一般do打頭的方法才是真正做事情的方法。

這裡面有一個比較關注的點就是,非單例模式下,對循環依賴的判斷 ,主要是三個方法:

用於注冊的容器是一個ThreadLocal,而存儲內容 如果只有一個beanName的話就是一個string ,而當一個線程生產多個bean實例時,就會轉為一個set來存儲所有創建中的beanName,這樣 就可以 通過方法判斷,當前需要生成的bean是否已經在創建中,如果已經在創建中,就可以判斷發生了循環依賴,這時就會直接報錯。

ps. 可以往下先看整個createBean的流程,反過來再看這里,可能會更好理解一點。

在非單例模式下,spring是無法解決循環依賴的,所以當判斷發生了循環依賴時,就會直接報錯 ,而在單例模式下,spring會採取一定的手段嘗試解決循環依賴,所以單例模式下的循環依賴的判斷與敬弊其他情況走了不同的邏輯(構造方法的單例模式循環依賴無法解決):

createBean中是實際創建實例的邏輯了,判斷是否無法解決的循環依賴是在getSingleton方法中完成的:

其中 singletonFactory.getObject(); 其實就是外面我們傳進來的createBean方法,是用於實際的創建對象的,而對循環依賴的判斷是通過這幾個方法解決的:

關於 inCreationCheckExclusions 我們先不管,這里判斷循環依賴主要用到的是singletonsCurrentlyInCreation這個set,如果是普通的屬性依賴即:

在創建A的時候,會通過addSingletonFactory這個方法將A提前暴露出去,然後,在populateBean中,發現需要B,這個時候B也會走到populateBean方法,然後又通過getBean方法獲取A,由於A已經提前暴露了(在getBean就可以直接從三級緩存拿到A的引用,而不用走beforeSingletonCreation的邏輯),所以B可以直接拿到A的引用從而解決了循環依賴。
而如果A和B是構造方法中互相依賴的話:

這個時候,情況又會大不一樣,在創建A的時候,會首先實例化A對象,在實例化的過程中,發現依賴了B(A提前暴露的邏輯是在實例化之後的,所以這個時候,A不會被添加到三級緩存中),這個時候就通過getBean獲取B對象,然後B對象實例化,發現依賴了A,從而再次通過getBean獲取A對象,由於A對象沒有在三級緩存中,所以會執行到上稿稿唯面說的beforeSingletonCreation方法中,可以想像到此時!this.singletonsCurrentlyInCreation.add(beanName)方法一定是true,從而拋出異常,這也就是為什麼在spring單例模式下無法解決構造方法循環依賴的原因。

可以看到 這里又是一堆邏輯,我們把經歷放在重點上也就是doCreateBean:

整個方法有四個關鍵點 :

非單例模式:

單例模式(構造器循環依賴):

單例模式(屬性循環依賴):

❽ Spring是如何解決循環依賴的

你需要我,我需要你就是循環依賴

在Spring中使用的三級緩存來解決循環依賴問題,這里的緩存其實就是Map對象

當獲取一個Bean時會先從緩存中查找是否有相應的Bean。

1 創建A實例

2 將A實例(半初始化,屬性沒有填充)暴露放入緩存中

3 填充A實例的屬性

4 A實例屬性依賴B對象

5 創建B對象實例

6 填充B實例屬性

7 B實例屬性依賴A對象

8 將上面已經暴露到三級緩存中的A對象注入給B實例

在獲取A對象的時候執行上面27.1中的getSingleton方法,會將三級緩存中A這個半初始化狀態的對象移除,將其存入到二級緩存中。

9 B實例Bean的創建工作繼續執行初始化方法

B如果需要AOP代理?最終B對象是個代理對象。B到此就完全的初始化完了,B的依賴對象A此時是個半初始化狀態的對象

10 B實例對象保存到一級緩存

最終B實例創建,初始化都執行完後會將自身加入到一級緩存同時清除二級,三級緩存

11 A實例Bean創建繼續執行

如果B是被AOP代理的那麼此時的A實例注入的B對象就是一個代理對象。

12 A實例Bean執行初始化方法

13 A繼續執行上面的10步驟

三級緩存解決問題:循環依賴+AOP問題

只用一,二級緩存:

從上面羅列的步驟看似乎很是完美解決了循環依賴問題,接下來我們看看加入AOP的場景

假如A,B兩個對象最終都是要被AOP代理的

執行到這里,A中依賴的B是代理對象沒有問題,但是B中依賴的A對象是原始對象;這就不正確了應該依賴的A也必須是代理對象才是。

引入三級緩存:

三級緩存引入了ObjectFactory對象,在獲取對象的時候,是調用ObjectFactory#getObject方法。

而這個getObject方法的實現實際執行的是getEarlyBeanReference方法,再來回顧下:

在創建實例時先將其存入三級緩存中:

getEarlyBeanReference方法就是提前創建代理對象

如果開啟了AOP代理後

通過getEarlyBeanReference方法提前創建代理對象。這樣就解決了循環依賴時AOP代理問題。保證獲取的都是同一個對象。

其實引入三級緩存還解決了一個問題就是延遲代理對象的創建,如果不應用ObjectFactory方式那麼我們需要不管需不需要都要先創建代理對象,而引入ObjectFactory可以在注入的時候先暴露的是ObjectFactory只有在調用getObject方法的時候才去創建真正的代理對象(避免了所有Bean都強制創建代理對象)。當沒有被代理時可以直接返回原始對象,如果被代理會提前創建代理對象。

不用二級直接是用一,三級緩存?

假設場景:A 依賴 B,B 依賴 A、C,C 依賴 A

如果這樣會出現不同的代理對象,每次調用getObject都會創建不同的代理對象(在上面的場景中如果只用一,三級緩存那麼 B 依賴 A會通過getObject獲取一個代理對象Proxy$1,接著注入C的時候 C中又依賴A,那這時候又從getObject獲取對象那麼返回的將又會是一個新的代理對象Proxy$2;在這個過程中A對象就出現了2個不一樣的對象了,這肯定是錯誤的)。而引入二級緩存也就解決了這個問題。只有二級緩存沒有的時候才從三級緩存匯總獲取(如果需要則創建代理對象,然後保存到二級緩存中,二級緩存中已經是提前創建了代理對象(如果需要代理))。

當一個Bean完全的創建完以後放入一級緩存中,此時會吧二級三級中的緩存清除。


完畢!!!!

SpringMVC參數統一驗證方法
SpringBoot多數據源配置詳解
SpringCloud Nacos 整合feign
Spring AOP動態代理失效的解決方法@Transactional為何會失效
SpringBoot配置文件你了解多少?
SpringBoot郵件發送示例 Springboot面試題整理附答案
SpringBoot項目查看線上日誌
在Spring Cloud 中你還在使用Ribbon快來試試Load-Balancer
SpringBoot分庫分表sharding-sphere3

熱點內容
格來雲伺服器到期 發布:2025-02-14 06:48:43 瀏覽:904
訂奧迪A7哪個配置比較好 發布:2025-02-14 06:44:23 瀏覽:139
spss的資料庫 發布:2025-02-14 06:37:32 瀏覽:119
sql除法運算 發布:2025-02-14 06:30:43 瀏覽:534
如何在家部署一台伺服器 發布:2025-02-14 06:22:04 瀏覽:433
u盤里文件夾是空的 發布:2025-02-14 06:13:22 瀏覽:803
安卓如何縮放圖片尺寸 發布:2025-02-14 06:06:34 瀏覽:116
六年級簡便演算法題 發布:2025-02-14 05:53:02 瀏覽:8
腳本精靈要root嗎 發布:2025-02-14 05:51:30 瀏覽:212
安卓手機如何錄屏怎麼去掉觸摸顯示 發布:2025-02-14 05:36:23 瀏覽:996