spring的條件裝配bean

1 應用程序環境的遷移java

問題:程序員

開發軟件時,有一個很大的挑戰,就是將應用程序從一個環境遷移到另外一個環境。web

例如,開發環境中不少方式的處理並不適合生產環境,遷移後須要修改,這個過程可能會莫名的出現不少bug,一個常常出如今程序員間有意思的問題是:在我那明明沒問題啊,爲何到你那就不行了?spring

舉個栗子,數據庫配置,在開發環境咱們可能使用一個嵌入式的數據源並在啓動的時候加載進來,可是在生產環境中這是糟糕的作法,咱們但願數據庫能直接從JNDI中獲取,或者是從鏈接池中獲取。那麼問題來了,當從開發環境遷移到生產環境中時,咱們應該怎麼作?sql

一個很好想到的辦法是:配置多個xml文件,每一個xml裏面配置一種數據源。而後在構建階段,肯定哪種數據源編譯到可部署的應用中。數據庫

可是這樣的作法缺點要爲每種環境從新構建應用,從開發階段遷移到QA(質量保證)階段肯能沒什麼大問題,可是從QA遷移到生產階段重新構建可能會出現bug,還QA個毛啊~ide

解答:測試

Spring爲環境遷移提供的解決方案是profile功能,爲bean配置profile註解,而後激活對應的profile。spa

怎麼配置profile?下面有兩種方式:code

(1)基於Java配置

@Configuration
public class DataSourceConfig {

    @Bean
    @Profile("dev")
    public DataSource embeddedDataSource() {

        // 配置開發環境     嵌入式數據源
        
    }


    @Bean
    @Profile("prod")
    public DataSource jndiDataSource() {

        // 配置生產環境    JNDI數據源
        
    }

}

(2)基於xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="
    http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee.xsd
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <beans profile="dev">
    <jdbc:embedded-database id="dataSource" type="H2">
      <jdbc:script location="classpath:schema.sql" />
      <jdbc:script location="classpath:test-data.sql" />
    </jdbc:embedded-database>
  </beans>
  
  <beans profile="prod">
    <jee:jndi-lookup id="dataSource"
      lazy-init="true"
      jndi-name="jdbc/myDatabase"
      resource-ref="true"
      proxy-interface="javax.sql.DataSource" />
  </beans>
</beans>

怎麼激活profile?

Spring在肯定哪一個profile處於激活狀態時,須要依賴兩個獨立的屬性:spring.profiles.active和spring.profiles.default。

若是設置了spring.profiles.active屬性的話,那麼它的值就會用來肯定哪一個profile是激活的。但若是沒有設置spring.profiles.active,那Spring將會查找spring.profiles.default的值。若是兩個屬性都沒配置,那麼被@Profile註解的類都不會被加載。

有多種方式來設置這兩個屬性:

  • 做爲DispatcherServlet的初始化參數;
  • 做爲Web應用的上下文參數;
  • 做爲JNDI條目;
  • 做爲環境變量;
  • 做爲JVM的系統屬性;
  • 在集成測試類上,使用@ActiveProfiles註解設置。

如何配置這些初始化參數不是本文討論的內容。但爲了把例子解釋清楚,這裏以web應用上下文參數爲例梳理整個流程,配置以下:

這裏默認激活配置爲dev的profile,因此啓動應用時,以Java配置爲例,註解@Profile("dev")標記的數據庫鏈接類會被加載到spring容器中,@Profile("prod")標記的類不會被加載。

profile是spring3.1中出現的新功能,只能用於環境遷移的條件裝配,可是spring4引入了Conditional功能,咱們能靈活的處理條件化裝配bean了,並且spring4也使用Conditional重構了profile,也就是說spring4以後,prefile是基於Conditional實現的。

 

 

2 條件裝配bean

假如你但願一個或多個bean只有在應用的類路徑下包含特定的庫時才建立。或者咱們但願某個bean只有當另外某個特定的bean也聲明瞭以後纔會建立。咱們還可能要求只有某個特定的環境變量設置以後,纔會建立某個bean。這些使用Conditional都能實現。

舉個例子:假設有一個名爲MagicBean的類,咱們但願只有設置了magic環境屬性的時候,Spring纔會實例化這個類。若是環境中沒有這個屬性,那麼MagicBean將會被忽略。

首先要有一個MagicBean,咱們不關注它的功能與實現,只須要知道有這個bean就行。接下來配置:

 

@Configuration
public class MagicConfig {

  @Bean
  @Conditional(MagicExistsCondition.class)
  public MagicBean magicBean() {
    return new MagicBean();
  }
  
}

 

只有知足MagicExistsCondition這個條件時,咱們才實例化MagicBeanMagicExistsCondition須要實現Condition接口。Condition接口裏面只有matches()方法,當實現類的matches()返回true時,條件才知足。不然條件不知足。MagicExistsCondition實現以下:

 

public class MagicExistsCondition implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    Environment env = context.getEnvironment();
    return env.containsProperty("magic");
  }
  
}

 

只有環境變量magic存在,matches纔會返回true,條件才知足,MagicBean纔會實例化。

 

參考文章《spring in action》

 

————完—————

相關文章
相關標籤/搜索