[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的設定在第一個範例也是可行的。