Spring Framework Core - The IoC Container (12) - 基於 java 的容器配置(2) 使用@Bean註解


本節的內容:如何使用註解配置 Spring 容器。包含下面的主題:

  • 基本概念:@Bean 和 @Configuration
  • 使用AnnotationConfigApplication初始化Spring容器
  • 使用@Bean註解
  • 使用@Configuration
  • 編寫基於java的配置
  • Bean定義概要
  • PropertySource抽象
  • 使用@PropertySource
  • 語句中佔位符的解析

12.3使用@Bean註解

@Bean是方法級別的註解,與XML bean元素作用相同。該註解支持一些XML的bean元素中的一些屬性,比如: * init-method * destroy-method * autowiring * 等屬性。

定義Bean

要聲明一個bean,可以使用@Bean註釋來註解一個方法。使用@Bean註解的方法註冊一個Bean定義作爲該方法的返回值,註冊的Bean在ApplicationContext的類型規範內。默認情況下,bean名與方法名相同。下面的例子顯示了一個@Bean方法聲明:

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

前面的配置與下面的Spring XML完全相同



<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>


這兩個聲明都使一個名爲transferService的bean在ApplicationContext中可用,綁定到類型爲TransferServiceImpl的對象實例,如下面的文本圖像所示:

   transferService -> com.acme.TransferServiceImpl 

您還可以使用接口(或基類)返回類型聲明@Bean方法,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

但是,這將高級類型預測的可見性限制爲指定的接口類型(TransferService)。然後,容器只知道一次完整類型(TransferServiceImpl),受影響的單例bean就被實例化了。非延遲性單例bean根據它們的聲明順序被實例化,因此您可能會看到不同的類型匹配結果,這取決於另一個組件何時試圖通過未聲明的類型進行匹配(例如@Autowired TransferServiceImpl,它只在transferService bean實例化之後纔會解析)。

注意

如果始終通過聲明的服務接口引用類型,則@Bean返回類型可以安全地與設計的意圖相吻合。但是,對於實現多個接口的組件或可能由其實現類型引用的組件,聲明最特定的返回類型會更安全。(至少與引用bean的注入點所要求的一樣具體)

Bean依賴關係

@bean註解的方法可以有任意數量的參數來描述構建該bean所需的依賴關係。
例如,如果我們的TransferService需要一個AccountRepository,我們可以用一個方法參數來實現這個依賴,如下面的例子所示:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

解析機制與基於構造的依賴注入非常相似。有關詳細信息,請參閱Constructor-based Dependency Injection。

接收生命週期回調

使用@Bean註解定義的任何類都支持常規的生命週期回調,並且可以使用JSR-250中的@PostConstruct和@PreDestroy註解。也完全支持常規的Spring生命週期回調。如果一個bean實現了InitializingBean、DisposableBean或Lifecycle,容器將調用它們各自的方法。標準的*Aware接口集(如BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware等)也得到了完全支持。
@Bean註釋支持指定任意初始化和銷燬回調方法,很像Spring XML在bean元素上的init-method和destroy-method屬性,如下面的示例所示:

public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

注意

默認情況下,Java配置定義的bean擁有public 修飾的close或shtudown方法將被自動加入到銷燬回調中。如果有public
修飾的close或shtudown方法,並且您不希望在容器關閉時調用它,那麼您可以向bean定義中添加@Bean(destroyMethod="")來禁用默認(推斷)模式。
在默認情況下,您可能希望對使用JNDI獲得的資源這樣做,因爲它的生命週期是在應用程序之外管理的。特別要注意的是,一定要始終對Datasource這樣做,因爲在JavaEE應用服務器上這樣做是有問題的。 下面的示例演示如何防止Datasource的自動銷燬回調

 @Bean(destroyMethod="")
  public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS"); } 

同樣,對於@Bean方法,您通常使用可編程的JNDI查找,可以使用Spring的JndiTemplate或JndiLocatorDelegate助手,或者直接使用JNDI InitialContext,但不使用JndiObjectFactoryBean變體(這將迫使您將返回類型聲明爲FactoryBean類型,而不是實際的目標類型,這使得在其他想要引用這裏提供的資源的@Bean方法中進行交叉引用調用時更加困難)。

對於上例中的BeanOne,在構造期間直接調用init()方法同樣有效,如下例所示

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }

    // ...
}

當您直接在Java中工作時,您可以對對象做任何您喜歡的事情,而不總是需要依賴容器生命週期。

確定Bean的作用域

Spring包含@Scope註解,因此您可以指定bean的範圍。
您可以指定使用@Bean註解定義的bean應該具有特定的範圍。您可以使用Bean Scope部分中指定的任何標準作用域。

Scope Description
singleton (默認)爲每個Spring IoC容器將單個bean定義作用於單個對象實例。
prototype 將單個bean定義作用於任意數量的對象實例。
request 將單個bean定義的範圍限定爲單個HTTP請求的生命週期。也就是說,每個HTTP請求都有自己的bean實例,這些實例是在單個bean定義的基礎上創建的。僅在可感知web的Spring應用程序上下文中有效。
session 將單個bean定義的範圍限定爲HTTP會話的生命週期。僅在可感知web的Spring應用程序上下文中有效。
application 將單個bean定義作用於ServletContext的生命週期。僅在可感知web的Spring應用程序上下文中有效。
websocket 將單個bean定義作用於WebSocket的生命週期。僅在可感知web的Spring應用程序上下文中有效。

從Spring 3.0開始,線程作用域可用,但默認情況下不註冊。有關更多信息,請參閱SimpleThreadScope的文檔。有關如何註冊此或任何其他自定義範圍的說明,請參閱Using a Custom Scope一章。

默認的範圍是單例的,但是你可以用@Scope註解來覆蓋它,如下面的例子所示:

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}

Spring通過作用域代理提供了處理作用域依賴項的方便方法。使用XML配置時創建這樣一個代理的最簡單方法是aop:scoped-proxy元素。用@Scope註釋在Java中配置bean可以提供與proxyMode屬性相同的支持。默認值是no proxy (ScopedProxyMode. no),但是您可以指定ScopedProxyMode。TARGET_CLASS或ScopedProxyMode.INTERFACES。

如果您使用Java將XML參考文檔中的作用域代理示例(參見作用域代理)移植到我們的@Bean,它類似於以下內容:

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}

定製化Bean命名

默認情況下,配置類使用@Bean方法的名稱作爲結果bean的名稱。但是,可以使用name屬性覆蓋此功能,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}

Bean別名

正如在bean的命名中所討論的,有時希望爲單個bean賦予多個名稱,稱爲bean別名。@Bean註釋的name屬性爲此接受一個字符串數組。下面的例子展示瞭如何爲一個bean設置多個別名:

@Configuration
public class AppConfig {

    @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }
}

Bean 描述

有時,提供bean的更詳細的文本描述是有幫助的。當出於監控目的而公開bean(可能通過JMX)時,這一點特別有用
要向@Bean添加描述,可以使用@Description註釋,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章