當前位置: 妍妍網 > 碼農

關於 Bean 容器的註入方式,99 % 的人都答不全!

2024-06-05碼農

引言:在使用 Spring 框架開發應用程式時,依賴註入是一個至關重要的概念。而對於 Bean 容器的註入方式,雖然我們可能都有一定的了解,但實際上很多人在被問及這個問題時可能並不能完整地回答。本文將深入探討 Spring 中 Bean 容器的註入方式,包括 XML 配置方式、基於註解方式、Java 配置方式以及自動掃描方式,並提供了詳細的程式碼範例和解析,以便讀者全面了解並掌握這些方式。

題目

關於 Bean 容器的註入方式,99 % 的人都答不全!
更多題目請見

推薦解析

1)XML配置方式

a) 基於內容註入

透過 <bean> 元素的內容來註入依賴。主要有以下幾種方式:

  • 構造器註入 使用 <constructor-arg> 元素配置建構函式參數。

    <bean id="exampleBean" class="com.example.ExampleBean">
    <constructor-arg ref="dependencyBean" />
    </bean>

  • Setter方法註入:使用 <property> 元素配置Setter方法註入的內容。

    <bean id="exampleBean" class="com.example.ExampleBean">
    <property name="dependencyBean" ref="dependencyBean" />
    </bean>

  • 直接量註入:使用 <constructor-arg> <property> 元素配置基本型別或字串等直接值。

    <bean id="exampleBean" class="com.example.ExampleBean">
    <constructor-arg value="exampleValue" />
    </bean>

  • b) 基於註解方式

    使用 @Autowired @Inject @Resource 註解在類或欄位上進行依賴註入。

  • 構造器註入:在類的構造器上使用 @Autowired 註解。

    @Component
    public classExampleBean{
    private DependencyBean dependency;
    @Autowired
    publicExampleBean(DependencyBean dependency){
    this.dependency = dependency;
    }
    }

  • Setter方法註入:在Setter方法上使用 @Autowired 註解。

    @Component
    public classExampleBean{
    private DependencyBean dependency;
    @Autowired
    publicvoidsetDependency(DependencyBean dependency){
    this.dependency = dependency;
    }
    }

  • 2)Java配置方式

    透過Java類來配置Bean,並使用 @Bean 註解將Bean註冊到Spring容器。

    @Configuration
    public classAppConfig{
    @Bean
    public ExampleBean exampleBean(){
    returnnew ExampleBean(dependencyBean());
    }
    @Bean
    public DependencyBean dependencyBean(){
    returnnew DependencyBean();
    }
    }

    3)自動掃描方式

    使用 <context:component-scan> @ComponentScan 註解來自動掃描和註冊帶有 @Component 及其衍生註解(如 @Service @Repository @Controller 等)的Bean。

    <context:component-scan base-package="com.example" />

    或者在Java配置類中使用:

    @Configuration
    @ComponentScan(basePackages = "com.example")
    public classAppConfig{
    // 配置其他Bean
    }

    4)註冊BeanPostProcessor

    實作 BeanPostProcessor 介面來在Bean初始化前後進行自訂處理,也可以用來動態註入Bean。

    @Component
    public classCustomBeanPostProcessorimplementsBeanPostProcessor{
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
    if (bean instanceof ExampleBean) {
    // 自訂處理邏輯,如動態註入其他依賴
    }
    return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {
    return bean;
    }
    }

    基於條件的 Bean 註冊

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    @Configuration
    public classAppConfig{
    @Bean
    @Conditional(WindowsCondition. class) // 註冊條件:Windows 作業系統
    publicOSInfowindowsInfo() 
    {
    returnnew OSInfo("Windows");
    }
    @Bean
    @Conditional(LinuxCondition. class) // 註冊條件:Linux 作業系統
    publicOSInfolinuxInfo() 
    {
    returnnew OSInfo("Linux");
    }
    }

    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    public classWindowsConditionimplementsCondition{
    @Override
    publicbooleanmatches(ConditionContext context, AnnotatedTypeMetadata metadata){
    String osName = System.getProperty("os.name");
    return osName.toLowerCase().contains("windows");
    }
    }

    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    public classLinuxConditionimplementsCondition{
    @Override
    publicbooleanmatches(ConditionContext context, AnnotatedTypeMetadata metadata){
    String osName = System.getProperty("os.name");
    return osName.toLowerCase().contains("linux");
    }
    }

    其他補充

    魚聰明 AI 的回答:

    魚聰明 AI 地址:https://www.yucongming.com/

    初始化階段的回呼

    1. InitializingBean 介面 InitializingBean 是 Spring 提供的一個介面,當 Bean 實作該介面時,Spring 容器在例項化 Bean 後會自動呼叫其 afterPropertiesSet() 方法,用於執行初始化邏輯。

      import org.springframework.beans.factory.InitializingBean;
      public classMyBeanimplementsInitializingBean{
      @Override
      publicvoidafterPropertiesSet()throws Exception {
      // 執行初始化邏輯
      }
      }

    2. @PostConstruct 註解 @PostConstruct 是一個標準的 Java 註解,用於在 Bean 初始化之後執行特定的方法。當一個方法被 @PostConstruct 註解標記時,Spring 容器會在例項化 Bean 後立即呼叫該方法。

      import javax.annotation.PostConstruct;
      public classMyBean{
      @PostConstruct
      publicvoidinit(){
      // 執行初始化邏輯
      }
      }

    銷毀階段的回呼

    1. DisposableBean 介面 DisposableBean 是 Spring 提供的一個介面,當 Bean 實作該介面時,Spring 容器在銷毀 Bean 之前會自動呼叫其 destroy() 方法,用於執行銷毀邏輯。

      import org.springframework.beans.factory.DisposableBean;
      public classMyBeanimplementsDisposableBean{
      @Override
      publicvoiddestroy()throws Exception {
      // 執行銷毀邏輯
      }
      }

    2. @PreDestroy 註解 @PreDestroy 是一個標準的 Java 註解,用於在 Bean 銷毀之前執行特定的方法。當一個方法被 @PreDestroy 註解標記時,Spring 容器會在銷毀 Bean 之前呼叫該方法。

      import javax.annotation.PreDestroy;
      public classMyBean{
      @PreDestroy
      publicvoidcleanup(){
      // 執行銷毀邏輯
      }
      }

    作用域(Scope)

    在 Spring 中,Bean 的作用域決定了在容器中建立的 Bean 的可見範圍和生命周期。Spring 框架提供了以下幾種標準的作用域:

    1. Singleton(預設) :在整個應用程式中只建立一個 Bean 例項,並在容器啟動時就進行建立。所有對該 Bean 的請求都將返回同一個例項。

    2. Prototype :每次請求該 Bean 時都會建立一個新的例項。這意味著每次透過容器獲取該 Bean 時,都會返回一個新的例項。

    3. Request :在每個 HTTP 請求中建立一個新的 Bean 例項,僅在 Web 應用程式上下文中有效。每個 HTTP 請求都會有自己的 Bean 例項,並且該例項僅在當前請求內有效。

    4. Session :在每個 HTTP Session 中建立一個新的 Bean 例項,也僅在 Web 應用程式上下文中有效。每個 HTTP Session 都會有自己的 Bean 例項,並且該例項僅在當前 Session 內有效。

    5. GlobalSession :在一個全域的 HTTP Session 中建立一個 Bean 例項,通常在分布式應用程式中使用。與 Session 作用域類似,但是在集群環境中只有一個 Bean 例項,僅在當前全域 Session 內有效。

    生命周期管理

    Spring 容器負責管理 Bean 的生命周期,確保它們在正確的時間點被建立、初始化、使用和銷毀。生命周期管理主要涉及以下幾個階段:

    1. 例項化 :Spring 容器根據配置資訊或註解建立 Bean 的例項。

    2. 內容設定 :Spring 容器將 Bean 的內容(依賴)註入到例項中。

    3. 初始化 :如果 Bean 實作了 InitializingBean 介面,容器會呼叫其 afterPropertiesSet() 方法,或者如果在方法上使用了 @PostConstruct 註解,則在內容設定後立即呼叫被註解標記的方法。

    4. 使用 :此時 Bean 例項已經完全初始化,可以被應用程式使用。

    5. 銷毀 :如果 Bean 實作了 DisposableBean 介面,容器會在銷毀 Bean 之前呼叫其 destroy() 方法,或者如果在方法上使用了 @PreDestroy 註解,則在 Bean 銷毀前呼叫被註解標記的方法。

    自訂作用域和生命周期管理

    除了上述提到的標準作用域和生命周期管理方式外,Spring 還允許開發者自訂作用域和生命周期管理策略。可以透過實作 Scope 介面來定義新的作用域,透過實作 BeanPostProcessor 介面來定義 Bean 的自訂初始化和銷毀邏輯。

    歡迎交流

    本文主要介紹 Bean 容器註入方式和條件註冊,另外有作用域和生命周期勾點函式的使用,面試中也經常會考實戰開發方面的知識,大家可以去關註一下,在文末還有三個問題,歡迎小夥伴在評論多多留言!

    1)Bean 容器註入方式有幾個種?

    2)Bean 的生命周期是怎麽樣的?

    3)怎麽實作基於條件的 Bean 註冊?


    點燃求職熱情!每周持續更新,海量面試題和大廠面經等你挑戰!趕緊關註面試鴨公眾號,輕松備戰春招和暑期實習!

    往期推薦