Java中如何實(shí)現(xiàn)IoC 分析依賴注入

ioc的核心是將對(duì)象創(chuàng)建和依賴管理交給外部容器,di通過構(gòu)造器、setter或接口注入實(shí)現(xiàn)。spring實(shí)現(xiàn)di需配置容器并定義bean,使用@autowired進(jìn)行注入,可通過構(gòu)造器(推薦)、setter(可選)或字段(不推薦)完成。Java配置用@configuration和@bean定義bean。啟動(dòng)流程包括定位資源、加載解析為beandefinition、注冊(cè)、實(shí)例化、注入、初始化至就緒狀態(tài)。循環(huán)依賴通過三級(jí)緩存解決:一級(jí)存完整bean,二級(jí)存早期bean,三級(jí)存objectfactory,僅支持單例bean。構(gòu)造器注入保證必需依賴,setter用于可選依賴,字段注入破壞封裝應(yīng)避免。

Java中如何實(shí)現(xiàn)IoC 分析依賴注入

IoC(控制反轉(zhuǎn))的核心在于將對(duì)象的創(chuàng)建和依賴關(guān)系的管理權(quán)從對(duì)象自身轉(zhuǎn)移到外部容器。依賴注入(DI)則是實(shí)現(xiàn)IoC的一種常用方式,它通過構(gòu)造器注入、Setter方法注入或接口注入,將依賴對(duì)象“注入”到目標(biāo)對(duì)象中,而非由目標(biāo)對(duì)象主動(dòng)創(chuàng)建。簡單來說,就是讓容器幫你new對(duì)象,并把對(duì)象需要的“零件”也給你裝好。

Java中如何實(shí)現(xiàn)IoC 分析依賴注入

解決方案

Java中如何實(shí)現(xiàn)IoC 分析依賴注入

Java中實(shí)現(xiàn)IoC/DI,通常會(huì)借助第三方框架,例如Spring、Guice、Dagger等。這里以Spring為例,講解如何實(shí)現(xiàn)依賴注入:

立即學(xué)習(xí)Java免費(fèi)學(xué)習(xí)筆記(深入)”;

Java中如何實(shí)現(xiàn)IoC 分析依賴注入

  1. 使用spring容器: 首先,你需要配置Spring容器。這通常通過xml配置、注解配置或者Java配置來實(shí)現(xiàn)。

  2. 定義Bean: 你需要定義哪些類由Spring容器來管理,這些類被稱為Bean。可以使用@Component、@Service、@Repository、@Controller等注解來標(biāo)記Bean,或者在XML配置中聲明Bean。

  3. 依賴注入: Spring提供了三種主要的依賴注入方式:

    • 構(gòu)造器注入: 通過類的構(gòu)造器來注入依賴。使用@Autowired注解在構(gòu)造器上,Spring會(huì)自動(dòng)找到匹配的Bean并注入。

      @Component public class MyService {     private final MyRepository myRepository;      @Autowired     public MyService(MyRepository myRepository) {         this.myRepository = myRepository;     }      // ... }
    • Setter方法注入: 通過Setter方法來注入依賴。使用@Autowired注解在Setter方法上。

      @Component public class MyService {     private MyRepository myRepository;      @Autowired     public void setMyRepository(MyRepository myRepository) {         this.myRepository = myRepository;     }      // ... }
    • 字段注入: 直接在字段上使用@Autowired注解。雖然方便,但不太推薦,因?yàn)樗茐牧祟惖?a >封裝性,并且在單元測試時(shí)可能會(huì)遇到問題。

      @Component public class MyService {     @Autowired     private MyRepository myRepository;      // ... }
  4. 使用@Qualifier解決歧義: 如果存在多個(gè)相同類型的Bean,Spring不知道應(yīng)該注入哪個(gè),這時(shí)可以使用@Qualifier注解來指定具體的Bean名稱。

    ```java @Component public class MyService {     private final MyRepository myRepository;      @Autowired     public MyService(@Qualifier("myRepositoryImpl1") MyRepository myRepository) {         this.myRepository = myRepository;     }      // ... } ```
  5. Java配置: 除了XML和注解,還可以使用Java配置來定義Bean和依賴關(guān)系。使用@Configuration注解標(biāo)記配置類,使用@Bean注解標(biāo)記Bean的創(chuàng)建方法。

    ```java @Configuration public class AppConfig {     @Bean     public MyRepository myRepository() {         return new MyRepositoryImpl();     }      @Bean     public MyService myService(MyRepository myRepository) {         return new MyService(myRepository);     } } ```

Spring IoC容器啟動(dòng)流程是怎樣的?

Spring IoC容器的啟動(dòng)流程可以大致分為以下幾個(gè)步驟:

  1. 定位資源: 容器首先需要定位到Bean定義的資源,例如XML配置文件、注解標(biāo)記的類等。
  2. 加載資源: 容器加載這些資源,并將Bean定義解析成Spring內(nèi)部的BeanDefinition對(duì)象。
  3. 注冊(cè)BeanDefinition: 容器將BeanDefinition注冊(cè)到BeanDefinitionRegistry中,這是一個(gè)Bean定義的注冊(cè)中心。
  4. 實(shí)例化Bean: 容器根據(jù)BeanDefinition創(chuàng)建Bean實(shí)例。這通常發(fā)生在第一次請(qǐng)求Bean時(shí),也可以配置成在容器啟動(dòng)時(shí)就預(yù)先實(shí)例化。
  5. 依賴注入: 容器將Bean所需的依賴注入到Bean實(shí)例中。
  6. 初始化Bean: 容器調(diào)用Bean的初始化方法(如果配置了),例如實(shí)現(xiàn)了InitializingBean接口的afterPropertiesSet()方法,或者使用@PostConstruct注解標(biāo)記的方法。
  7. Bean準(zhǔn)備就緒: Bean實(shí)例創(chuàng)建完成,可以被應(yīng)用程序使用了。

構(gòu)造器注入、Setter注入和字段注入,應(yīng)該選擇哪種?

這三種注入方式各有優(yōu)缺點(diǎn):

  • 構(gòu)造器注入: 強(qiáng)制依賴,保證對(duì)象創(chuàng)建時(shí)所有必需的依賴都已就緒。有利于對(duì)象的不可變性。推薦使用。
  • Setter注入: 允許可選依賴,可以在對(duì)象創(chuàng)建后動(dòng)態(tài)設(shè)置依賴。但容易出現(xiàn)依賴未設(shè)置的情況。
  • 字段注入: 最簡單,但破壞了封裝性,不利于單元測試。不推薦使用。

總體來說,構(gòu)造器注入是最佳選擇,因?yàn)樗軌虮WC對(duì)象的完整性和不可變性。如果存在可選依賴,可以考慮Setter注入。盡量避免字段注入。

循環(huán)依賴問題如何解決?

循環(huán)依賴是指兩個(gè)或多個(gè)Bean之間相互依賴,形成一個(gè)循環(huán)引用。例如,A依賴B,B依賴C,C又依賴A。Spring IoC容器可以通過以下方式解決循環(huán)依賴問題:

  • 提前暴露Bean: Spring在創(chuàng)建Bean時(shí),會(huì)提前將Bean的ObjectFactory暴露出來,以便其他Bean可以引用。這樣,即使Bean還沒有完全創(chuàng)建完成,也可以被其他Bean引用。
  • 三級(jí)緩存: Spring使用三級(jí)緩存來解決循環(huán)依賴問題。
    • 一級(jí)緩存 (singletonObjects): 存放完全初始化好的Bean。
    • 二級(jí)緩存 (earlySingletonObjects): 存放提前暴露的Bean,這些Bean可能還沒有完全初始化完成。
    • 三級(jí)緩存 (singletonFactories): 存放Bean的ObjectFactory,用于創(chuàng)建Bean實(shí)例。

當(dāng)出現(xiàn)循環(huán)依賴時(shí),Spring會(huì)首先嘗試從一級(jí)緩存中獲取Bean,如果獲取不到,則嘗試從二級(jí)緩存中獲取,如果還獲取不到,則從三級(jí)緩存中獲取ObjectFactory,并使用ObjectFactory創(chuàng)建Bean實(shí)例。創(chuàng)建Bean實(shí)例后,將Bean放入二級(jí)緩存,并移除三級(jí)緩存中的ObjectFactory。當(dāng)Bean完全初始化完成后,將Bean從二級(jí)緩存移動(dòng)到一級(jí)緩存。

需要注意的是,Spring只能解決單例Bean的循環(huán)依賴問題,對(duì)于原型Bean,由于每次獲取Bean都是一個(gè)新的實(shí)例,因此無法解決循環(huán)依賴問題。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊6 分享