spring boot環境抽象

dust1發表於2019-04-02

在實際開發中,開發人員在編寫springboot的時候通常要在本地環境測試然後再部署到Production環境,這兩種環境一般來講是不同的,最主要的區別就是資料來源的不同。

在應用環境中,整合在容器的抽象環境模型有兩個方面:profiles和properties。只有給出的profile被啟用,一組邏輯命名的bean定義才會在容器中註冊。

環境變數物件角色和profiles的關係來決定哪個profiles(如果有)處於當前啟用狀態,哪個profiles預設被啟用。

@Profile

基於Java類的環境配置

@Profile註解可以用來標註@Configuration註解的類。表示該特定環境下啟用該類下的所有bean。當然也可以專門用來標註@Bean,因為許多時候本地環境和Production環境的區別只是資料來源不同罷了。

@Configuration
public class ProfileConf {


    @Bean
    @Profile("dev")
    public UserInfo devUserInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("dev");
        return userInfo;
    }

    @Bean
    @Profile("production")
    public UserInfo productionUserInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("production");
        return userInfo;
    }

}
複製程式碼

啟用profile

現在我們已經更新了我們的配置,我們仍然需要說明哪個profile是啟用的。如果直接註冊@Configuration標註的類,這將會看到一個NoSuchBeanDefinitionException被丟擲,因為容器找不到一個對應的環境下的bean。

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.register(UserConf.class);
        context.refresh();
        System.out.println(context.getBean(UserInfo.class));
    }
複製程式碼

預設的profile

預設配置檔案表示預設啟用的配置檔案。

    @Bean
    @Profile("default")
    public UserInfo defaultUserInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("default");
        return userInfo;
    }
複製程式碼

如果沒有profile是啟用狀態,上面的bean將會被建立;這種方式可以被看做是對一個或者多個bean提供了一種預設的定義方式。如果啟用任何的profile,那麼預設的profile都不會被應用。

屬性源抽象

Spring 環境抽象提供了可配置的屬性源層次結構的搜尋操作。

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//        context.getEnvironment().setActiveProfiles("dev");
        context.getEnvironment().setActiveProfiles("dev");
        context.register(ProfileConf.class);
        context.refresh();
        ConfigurableEnvironment environment = context.getEnvironment();
        Map<String, Object> maps = environment.getSystemProperties();
        maps.keySet().forEach(k -> System.out.println(k + "->" + maps.get(k)));

        System.out.println("===========================");
        Map<String, Object> environment1 = environment.getSystemEnvironment();
        environment1.keySet().forEach(k -> System.out.println(k + "->" + environment1.get(k)));

        System.out.println(environment.containsProperty("java.vm.version"));
    }
複製程式碼

在上面的例子中可以獲取Environment的兩個系統變數以及環境變數。

一個PropertySource是對任何key-value資源的簡單抽象,並且Spring 的標準環境是由兩個PropertySource配置的,一個表示一系列的JVM 系統屬性(System.getProperties()),一個表示一系列的系統環境變數(System.getenv())。

具體的說,當使用StandardEnvironment時,如果在執行時系統屬性或者環境變數中包括foo,那麼呼叫env.containsProperty(“java.vm.version”)方法將會返回true。

更重要的是,整個機制都是可配置的。也許你有個自定義的屬性來源,你想把它整合到這個搜尋裡面。這也沒問題,只需簡單的實現和例項化自己的PropertySource,並把它新增到當前環境的PropertySources集合中:

    ConfigurableApplicationContext ctx = new GenericApplicationContext();
    MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
    sources.addFirst(new MyPropertySource());
複製程式碼

@PropertySource

上一篇文章講到,基於Java的配置很多時候會和xml混合使用。其中@Import還可以匯入其他Java配置類,這裡要說的@PropertySource註解表示匯入.properties檔案。

@Configuration
@PropertySource("classpath:user.properties")
public class UserConf {

    @Autowired
    Environment environment;

    @Bean
    //每次呼叫就建立一個新的bean
    @Scope("prototype")
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(Integer.valueOf(environment.getProperty("user.id")));
        System.out.println(environment.getProperty("user.name"));
        userInfo.setName(environment.getProperty("user.name"));
        return userInfo;
    }

}
複製程式碼
user.id=11
user.name=asdasd
複製程式碼

任何出現在@PropertySource中的資源位置佔位符都會被註冊在環境變數中的資源解析。

假設”user.name”已經在其中的一個資源中被註冊,例如:系統屬性或環境變數,佔位符將會被正確的值解析。

如果沒有,”default/path”將會使用預設值。如果沒有預設值,而且無法解釋屬性,則丟擲IllegalArgumentException異常。

相關文章