注入設定檔


在〈使用 Spring DI〉中,初步使用了 Spring DI 的 JavaConfig 以及自動綁定功能,不過,AppConfig 中的 JDBC URL、名稱、密碼等是寫死的,雖然 AppConfig 應看成是設定檔的角色,然而,畢竟它是個 .java,會編譯為 .class,某些設定若能不寫死,還是比較方便更改設定的。

Spring 中,可以使用 @Value 來為字串注入值,可以將 AppConfig 修改為:

package cc.openhome;

import javax.sql.DataSource;

import org.h2.jdbcx.JdbcDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan
@PropertySource("classpath:jdbc.properties")
public class AppConfig { 
    @Value("jdbc:h2:tcp://localhost/c:/workspace/SpringDI2/gossip")
    private String jdbcUrl;

    @Value("caterpillar")
    private String user;

    @Value("12345678")
    private String password;

    @Bean
    public DataSource getDataSource() {
        JdbcDataSource dataSource = new JdbcDataSource();
        dataSource.setURL(jdbcUrl);
        dataSource.setUser(user);
        dataSource.setPassword(password);
        return dataSource;
    }
}

呃?這算什麼?然而是在 .java 中寫死了值啊!〈在 DI 之前〉中載入屬性檔案的部份呢?這可以透過 PropertySourcesPlaceholderConfigurer 實例讀取,並提供給 Spring 注入指定的屬性之用:

package cc.openhome;

import javax.sql.DataSource;

import org.h2.jdbcx.JdbcDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

@Configuration
@ComponentScan
@PropertySource("classpath:jdbc.properties")
public class AppConfig { 
    @Value("${cc.openhome.jdbcUrl}")
    private String jdbcUrl;

    @Value("${cc.openhome.user}")
    private String user;

    @Value("${cc.openhome.password}")
    private String password;

    @Bean
    public DataSource getDataSource() {
        JdbcDataSource dataSource = new JdbcDataSource();
        dataSource.setURL(jdbcUrl);
        dataSource.setUser(user);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer
                       propertySourcesPlaceholderConfigurer() {
       return new PropertySourcesPlaceholderConfigurer();
    }    
}

PropertySourcesPlaceholderConfigurer 實例是 Spring 本身需要的 Bean,通常你不需要在應用程式中直接取用它,也就是說,設定檔中除了設定應用程式需要的 Bean 之外,也會用來設定 Spring 本身用到的資源。

留意一下 static,那是必要的,這是因為 PropertySourcesPlaceholderConfigurer 實作了 BeanFactoryPostProcessor 介面,BeanFactoryAnnotationConfigApplicationContext 實現的介面之一)會在載入 Bean 定義檔之後,還沒生成 Bean 實例之前,執行 BeanFactoryPostProcessor 定義的 postProcessBeanFactory 方法。

如果使用 XML 方式定義 PropertySourcesPlaceholderConfigurer,其實是不用太在意這個過程,因為撰寫設定方式,基本上與其他 Bean 沒有差異。

然而,使用 JavaConfig 的方式設定時,這就意謂著 @Configuration 標註的類別在載入之後,生成實例之前,就必須能取得 PropertySourcesPlaceholderConfigurer 實例,因而 Spring 在這部份是透過 static 方法來解決。

PropertySourcesPlaceholderConfigurer 要讀取的設定檔來源,可以透過 @PropertySource 來指定,在上例中,@PropertySource("classpath:jdbc.properties") 表示,從類別路徑讀取 jdbc.properties:

cc.openhome.jdbcUrl=jdbc:h2:tcp://localhost/c:/workspace/SpringDI2/gossip
cc.openhome.user=caterpillar
cc.openhome.password=12345678

而在 @Value 的部份,值的設定使用了 ${...} 的 PlaceHolder 形式,如此一來,就可以將 .properties 中對應的值注入。

PlaceHolder 形式也可以設定預設值,例如 @Value("${cc.openhome.user:caterpillar}") 的話,在沒有對應的 cc.openhome.user 時,就會使用 "caterpillar" 這個值。

你可以在 SpringDI2 找到以上的範例專案。