《Spring實戰》-第三章:Bean的高級裝配(1)-環境Profile

慢慢來比較快,虛心學技術

1.爲什麼需要profile?

實際開發中,生產環境和開發環境通常都不會是同一個,最明顯的例子就是,生產環境(正式環境)的數據庫和開發環境的數據庫一般都是不一樣的。那麼爲了方便配置的轉換,Spring提供了profile關鍵字進行統一的環境配置,通過profile配置我們可以快速的切換整個應用的環境,而不需要從代碼層面進行重新構建

那麼,使用profile的目的是爲了更方便的配置不同的環境,指定某些具體的類或組件的作用域

注:每個bean或組件都有默認的作用域default,如果沒有限定作用域,則這些bean都會被裝配,如果指定了作用域,則只有處在相應作用域(環境)中時纔會被裝配

2.怎麼配置profile?

JavaConfig顯式裝配或者自動裝配可以通過註解@Profile進行環境限制,比如:

Ⅰ、自動化裝配,我們將類DataSource的作用域限定爲只有當環境爲“prod”才被裝配,結合@Component註解和@Profile註解實現

代碼1.1

@Component
@Profile("prod")
public class DataSource {

    /**
     * 定義名稱
     */
    private String name;

    public DataSource(){
        super();
    }

    public DataSource(String name){
       this.name = name;
    }
}

Ⅱ、JavaConfig顯式配置,有兩種實現方式

  • 第一種:在指定多個配置類,在配置類上指定作用域,其內所定義的所有Bean只能在該作用域下被裝配,進行環境統一配置
代碼1.2

//1.創建一個配置類DevConfig,並將其作用域設置爲dev,則其內定義的Bean只有在環境爲dev的時候纔可以被裝載
@Configuration
@Profile(value = "dev")
public class DevConfig {

    //定義一個name屬性爲Dev DataSource的Bean
    @Bean
    public DataSource dataSource(){
        return new DataSource("Dev DataSource");
    }
}

//2.創建一個配置類ProdConfig,並將其作用域設置爲prod,則其內定義的Bean只有在環境爲prod的時候纔可以被裝載
@Configuration
@Profile(value = "prod")
public class ProdConfig {

    //定義一個name屬性爲Dev DataSource的Bean
    @Bean
    public DataSource dataSource(){
        return new DataSource("Prod DataSource");
    }
}

//3.創建一個默認配置類,當系統未指定系統環境時,將默認使用該配置
@Configuration
public class DefaultConfig {
    @Bean
    public DataSource dataSource(){
        return new DataSource("Default DataSource");
    }
}

//4.創建一個統一配置類SystemConfig,並將三個配置類引入,因爲各自定義了作用域(DefaultConfig未明文指定),所以引入並不會導致衝突
@Configuration
@Import({DevConfig.class,ProdConfig.class,DefaultConfig.class})
public class SystemConfig {}
  • 第二種:除了在類上使用@Profile指定類作用域之外,@Profile還可以在方法上使用,在JavaConfig結合@Bean進行裝配環境的限制
代碼1.3

@Configuration
public class SystemConfig {

    @Bean
    @Profile("dev")//當系統環境爲dev的時候,使用該Bean方法進行裝配
    public DataSource devDataSource(){
        return new DataSource("Dev DataSource");
    }

    @Bean
    @Profile("prod")//當系統環境爲prod的時候,使用該Bean方法進行裝配
    public DataSource prodDataSource(){
        return new DataSource("Prod DataSource");
    }

    @Bean//默認裝配
    public DataSource defaultDataSource(){
        return new DataSource("Default DataSource");
    }
}

Ⅲ、XML配置Profile,和JavaConfig類似也有兩種方式

  • 第一種:爲每一個配置文件的<beans>配置Profile,只有在固定對應環境下配置文件纔會生效(比較常用)
代碼1.4

//1、創建配置文件DevConfig.xml,並在<beans>節點設置profile屬性爲dev,只有在環境爲dev的時候本配置文件生效
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
   profile="dev"
    >
    <bean class="com.my.spring.bean.DataSource" name="dataSource">
        <constructor-arg name="name" value="Dev DataSource"></constructor-arg>
   </bean>
</beans>

//2、創建配置文件ProdConfig.xml,並在<beans>節點設置profile屬性爲prod,只有在環境爲prod的時候本配置文件生效
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
   profile="prod"
     >
    <bean class="com.my.spring.bean.DataSource" name="dataSource">
        <constructor-arg name="name" value="Prod DataSource"></constructor-arg>
    </bean>
</beans>

//3、創建配置文件DefaultConfig.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.my.spring.bean.DataSource" name="dataSource">
        <constructor-arg name="name" value="Default DataSource"></constructor-arg>
    </bean>

</beans>

//4、創建系統配置文件,將上述三個配置文件引入,因爲限定了環境,所以並不會衝突
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="DefaultConfig.xml"/>
    <import resource="DevConfig.xml"/>
    <import resource="ProdConfig.xml"/>
</beans>
  • 第二種:在同一個配置文件中指定不同的<beans>設定profile屬性,所包裹的bean只有在指定環境下才會被裝配
代碼1.5

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

    <description>Spring配置文件</description>

    <beans>
        <bean id="dataSource" name="default" class="com.my.spring.bean.DataSource">
            <constructor-arg name="name" value="Default DataSource"></constructor-arg>
        </bean>
    </beans>

    <beans profile="dev">
        <bean id="dataSource" name = "dev" class="com.my.spring.bean.DataSource">
            <constructor-arg name="name" value="Dev DataSource"></constructor-arg>
        </bean>
    </beans>

    <beans profile="prod">
        <bean id="dataSource"  name="prod" class="com.my.spring.bean.DataSource">
            <constructor-arg name="name" value="Prod DataSource"></constructor-arg>
        </bean>
    </beans>
</beans>

3、如何激活profile?

如上述配置每個配置文件或者JavaConfig,Bean等的profile屬性後,如何設定一個系統所處的環境呢?

Spring提供了多種方式:

作爲 DispatcherServlet 的初始化參數;

作爲 Web 應用的上下文參數;

作爲 JNDI 條目;

作爲環境變量;

作爲 JVM 的系統屬性;

在集成測試類上,使用 @ActiveProfiles 註解設置;

  • 此處先了解@ActiveProfiles註解方式

在上一章中,我們瞭解到,Spring測試使用@RunWith(SpringJUnit4ClassRunner.class)和@ContextConfiguration註解進行應用上下文注入測試,在測試中我們可以通過結合@ActiveProfiles註解進行測試環境的配置,具體代碼如下:

以自動化裝配模式爲例:

上述代碼1.1中指定DataSource只有在prod環境下才會被裝載,故在測試中將系統環境分別指定爲prod和dev測試DataSource的Bean是否會裝配

代碼1.6

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {ComponentConfig.class})
@ActiveProfiles("prod")//指定系統環境爲prod
public class AppComponentTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testGetBean(){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        for(String  beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }
}

測試結果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
componentConfig
dataSource //dataSource被裝配

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {ComponentConfig.class})
@ActiveProfiles("dev")//指定系統環境爲dev
public class AppComponentTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testGetBean(){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        for(String  beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }
}

//dataSource未被裝配
測試結果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
componentConfig

再以JavaConfig顯式裝配爲例,設定不同的profile測試獲取到的dataSource是否一致,以代碼1.3作爲系統配置:

代碼1.7

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {SystemConfig.class})
@ActiveProfiles("prod")//指定系統配置環境爲prod
public class AppConfigTest {

    @Autowired
    private ApplicationContext applicationContext;
    @Test
    public void testGetBean(){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        for(String  beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }
}

測試結果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
systemConfig
prodDataSource //證明獲取的是prod環境的dataSource

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {SystemConfig.class})
@ActiveProfiles("dev")//指定系統配置環境爲dev
public class AppConfigTest {

    @Autowired
    private ApplicationContext applicationContext;
    @Test
    public void testGetBean(){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        for(String  beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }
}

測試結果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
systemConfig
devDataSource //證明獲取的是dev環境的dataSource
  • 由於未瞭解到網絡模塊,所以web配置方式暫不贅述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章