[spring]從頭開始-Profiles

開發工具:IntelliJ IDEA 13

開發環境:jdk1.6.0_45

Framework:spring 3

這是 spring 3.1.x 所帶來的新的概念,依據程式需求自行設定所採用的 Profiles

我們以學校供餐為一個例子,Food 類別(normal class)為餐食,FoodProviderService 提供菜單(interface),而菜單提供者這邊先用兩個,分別是 foo.bar.highschoo.FoodProviderServiceImpl 及 foo.bar.kindergarten.FoodProviderServiceImpl,分別有其對應的 spring configuration file,因為我們之前有提過 configuration 有 xml 及 java code 兩種方式,我們先示範第一種用 xml 配置的方式:

Food.java

package foo.bar;

/**

 * Created by Hsu on 2014/7/16.

 */

public class Food {

    private String name;

    public Food() {

    }

    public Food(String name) {

        this.name = name;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

}

FoodProviderService.java

package foo.bar;

import java.util.List;

/**

 * Created by Hsu on 2014/7/16.

 */

public interface FoodProviderService {

    public List<Food> provideLunchSet();

}

foo.bar.highschool.FoodProviderServiceImpl.java

package foo.bar.highschool;

import foo.bar.Food;

import foo.bar.FoodProviderService;

import java.util.ArrayList;

import java.util.List;

/**

 * Created by Hsu on 2014/7/16.

 */

public class FoodProviderServiceImpl implements FoodProviderService {

    @Override

    public List<Food> provideLunchSet() {

        List<Food> lunchSet = new ArrayList<Food>();

        lunchSet.add(new Food("Coke"));

        lunchSet.add(new Food("Hamburger"));

        lunchSet.add(new Food("French Fries"));

        return lunchSet;

    }

}

foo.bar.kindergarten.FoodProviderServiceImpl.java

package foo.bar.kindergarten;

import foo.bar.Food;

import foo.bar.FoodProviderService;

import java.util.ArrayList;

import java.util.List;

/**

 * Created by Hsu on 2014/7/16.

 */

public class FoodProviderServiceImpl implements FoodProviderService {

    @Override

    public List<Food> provideLunchSet() {

        List<Food> lunchSet = new ArrayList<Food>();

        lunchSet.add(new Food("Milk"));

        lunchSet.add(new Food("Biscuits"));

        return lunchSet;

    }

}

配置檔 highschool-config.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:context="http://www.springframework.org/schema/context"

       xmlns:p="http://www.springframework.org/schema/p"

       xmlns:c="http://www.springframework.org/schema/c"

       xmlns:util="http://www.springframework.org/schema/util"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.1.xsd"

        profile="highschool">

    <bean id="foodProviderService"

          class="foo.bar.highschool.FoodProviderServiceImpl"/>

</beans>

配置檔 kindergarten-config.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:context="http://www.springframework.org/schema/context"

       xmlns:p="http://www.springframework.org/schema/p"

       xmlns:c="http://www.springframework.org/schema/c"

       xmlns:util="http://www.springframework.org/schema/util"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.1.xsd"

        profile="kindergarten">

    <bean id="foodProviderService"

          class="foo.bar.kindergarten.FoodProviderServiceImpl"/>

</beans>

演繹類別:

ProfileXmlConfigExample.java

package foo.bar;

import org.springframework.context.support.GenericXmlApplicationContext;

import java.util.List;

/**

 * Created by Hsu on 2014/7/16.

 */

public class ProfileXmlConfigExample {

    public static void main(String[] args) {

        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

        //choice kindergarten profiles

        ctx.getEnvironment().setActiveProfiles("kindergarten");

        //choice highschool profiles

        //ctx.getEnvironment().setActiveProfiles("highschool");

        //the ctx.load() method will load both kindergarten-config.xml and highschool-config.xml

        ctx.load("classpath:*-config.xml");

        ctx.refresh();

        FoodProviderService foodProviderService =

                ctx.getBean("foodProviderService", FoodProviderService.class);

        List<Food> lunchSet = foodProviderService.provideLunchSet();

        for (Food food: lunchSet) {

            System.out.println("Food: " + food.getName());

        }

    }

}

上面code 中有幾個部份值得注意,spring 要如何知道哪一個是誰的配置檔?因為我們在 ctx.load() 時,載入了 *-config.xml 的檔案,也就是都載入了,所以我們要在各置的配置檔中設定 profile="highschool" profile="kindergarten" 這樣一來在 ctx.getEnvironment().setActiveProfiles("highschool") or ctx.getEnvironment().setActiveProfiles("kindergarten") 時,spring 才會知道要載入哪一個配置檔,並執行我們後續的作業  foodProviderService.provideLunchSet()

這一個範例的執行結果如下:

setActiveProfiles("kindergarten")

Food: Milk

Food: Biscuits

or

setActiveProfiles("highschool")

Food: Coke

Food: Hamburger

Food: French Fries

這邊我們可以看到,當載入不一樣的配置檔時,有不一樣的顯示結果出現,代表profiles的設定是沒問題的。

接下來我們改用 java code 的方式去實現一樣的效果,前面有提到config-class 需要使用 @Configuration 及 @Bean 範例如下

HighschoolConfig.java

package foo.bar;

import foo.bar.highschool.FoodProviderServiceImpl;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Profile;

/**

 * Created by Hsu on 2014/7/16.

 */

@Configuration

// like xml configuration profile="highschool"

@Profile(value = "highschool")

public class HighschoolConfig {

    @Bean

    // like <bean id="foodProviderService" class="foo.bar.highschool.FoodProviderServiceImpl"/>

    public FoodProviderService foodProviderService(){

        return new FoodProviderServiceImpl();

    }

}

KindergartenConfig.java

package foo.bar;

import foo.bar.kindergarten.FoodProviderServiceImpl;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Profile;

/**

 * Created by Hsu on 2014/7/16.

 */

@Configuration

// like xml configuration profile="kindergarten"

@Profile(value = "kindergarten")

public class KindergartenConfig {

    @Bean

    // like <bean id="foodProviderService" class="foo.bar.kindergarten.FoodProviderServiceImpl"/>

    public FoodProviderService foodProviderService(){

        return new FoodProviderServiceImpl();

    }

}

上面可以看到兩個 java configuration file 皆引入不同 package 的 FoodProviderServiceImpl,並且於 @Profile(value = "highschool") 設定該配置檔所對應的名稱(profile name)。

最後是相對應的演繹類別:

ProfileJavaConfigExample.java

package foo.bar;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.List;

/**

 * Created by Hsu on 2014/7/16.

 */

public class ProfileJavaConfigExample {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

        ctx.getEnvironment().setDefaultProfiles("highschool");

        // ctx.getEnvironment().setDefaultProfiles("kindergarten");

        // can set Java VM options to -Dspring.profiles.active="kindergarten" have the same result

        ctx.register(KindergartenConfig.class, HighschoolConfig.class);

        ctx.refresh();

        FoodProviderService foodProviderService = ctx.getBean("foodProviderService", FoodProviderService.class);

        List<Food> lunchSet = foodProviderService.provideLunchSet();

        for (Food food: lunchSet) {

            System.out.println("Food: " + food.getName());

        }

    }

}

上面可以注意到是一樣是採用 ctx.getEnvironment().setDefaultProfiles("highschool") 來設定對應的 profile,但不同的是不是採用 ctx.load() 的方式載入profiles,而是使用 ctx.register() 的方式載入 profiles,還有須注意到的是兩個範例的 ApplicationContext 的類別是不同的,第一個是一般的 GenericXmlApplicationContext 第二種因為用到 Annotation 所以使用 AnnotationConfigApplicationContext 這的類別比較方便,兩個使用的方不太相同,須注意一下。

範例的執行結果:

Food: Coke

Food: Hamburger

Food: French Fries

值行的結果與第一個用 xml 設置 profiles 的範例是相同的。

第二個範例中有一行的註解為 can set Java VM options to -Dspring.profiles.active="kindergarten" have the same result 這是說,要是不使用 setDefaultProfiles 的方式來切換 profiles,其實也可以在執行階段加入 VM 參數去決定使用哪一個 profiles,以上VM的設定在第一個範例也是可行的。

demo程式(右鍵另開視窗下載)