Bean 組態


在定義 JavaConfig 時,@ComponentScan 預設會掃描同一套件或其子套件中,是否有標註相關元件,你也可以指定要掃描的套件,例如 @ComponentScan("cc.openhome.controller"),如果有多個套件要掃描,可以使用 basePackages 指定,例如:

@ComponentScan(basePackages = {"cc.openhome.model", "cc.openhome.controller"})

如果要指定掃描元件類別及其所在套件,可以使用 basePackageClasses,例如:

@ComponentScan(basePackageClasses = {
     cc.openhome.model.UserService.class, 
     cc.openhome.controller.AccountController.class
})

如果想進一步包含或排除某些元件,可以指定 includeFiltersexcludeFilters,指定時使用 ComponentScan.Filter 來設定,最基本的是設定 classes,例如:

@ComponentScan(
    basePackages = {"cc.openhome.model", "cc.openhome.controller"},
    excludeFilters = {@Filter(classes = {MailService.class})}
)

使用 @Bean 定義 Bean 的時候,預設會使用方法的名稱作為 Bean 的名稱,可以使用 ApplicationContextgetBean 指定 Bean 名稱來取得對應的 Bean,不過傳回型態為 Object,就這點來看,透過指定類別還是比較方便安全的。

使用 @Bean 時,可以自行指定名稱,例如 @Bean("embeddedDatasource") 或者是 @Bean(name = "embeddedDatasource"),實際上可以指定多個名稱,例如 @Bean({"d1", "d2"})

如果原始碼在專案的控制之中,標註 @Component 時也可以指定名稱,例如 @Component("userService"),若不指定,預設為類別名稱首字母小寫。

如果 Bean 本身具有初始或清除資源的方法,可以透過 initMethoddestroyMethod 指定方法名稱,例如〈設定 Profile〉中就有以下的設定:

@Bean(destroyMethod="shutdown")
public DataSource dataSource(){
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("classpath:schema.sql")
            .addScript("classpath:testData.sql")
            .build();
}

@Bean 可以搭配 @Lazy 來標註,該 Bean 就延遲初始。對於 Spring 管理的 Bean,預設都是單例(Singleton),也就是整個應用程式只存在一個實例,如果想要每次取得 Bean 時,都必須建立一個新的實例,可以標註 @Scope("prototype")@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)ConfigurableBeanFactory.SCOPE_PROTOTYPE 只是個字串值 "prototype")。

如果原始碼在專案的控制之中,也可以在被 @Component 標註的元件中標註 @Scope("prototype")

在複雜的環境下,也許你對想要依某些條件,決定某個 Bean 是否生成,這時可以使用 @Conditional,它可以指定實現了 Condition 介面的類別,例如:

@Bean(destroyMethod="shutdown")
@Conditional(DbCond.class)
public DataSource dataSource(){
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("classpath:schema.sql")
            .addScript("classpath:testData.sql")
            .build();
}

DbCond 是實現了 Condition 的類別,其實作的 matches(ConditionContext context, AnnotatedTypeMetadata metadata) 方法,可以傳回 truefalse,告知 Spring 是否建立該 Bean。

每個標註的相關設定,都可以在 API 文件上找到說明,如果有某個需求,應該試著自行查看 API 文件,看看有無對應的屬性可以設定。例如,也許想要延遲初始 @ComponentScan 掃描到的元件,就可以在 ComponentScan API 文件找找看,或許有個屬性就可以滿足需求(實際上就是 lazyInit 屬性)。