81
技術社區[雲棲]
spring的Profile使用對比和應用場景分析
spring中存在這樣一個功能,通過Profile來選擇不同環境下的不同配置,說白了,就是通過設置一個參數來選擇使用不同的數據,這個數據可能是一個bean,可能是一個xml文件,也有可能是一個propertes文件。
經過代碼演練和測試,我大體知道了這個功能是幹嘛的,也初步知道了它的幾種實現方式,但是實際上我依然不是十分明白它的優勢和好處在何處,因為根據自己以往的項目經驗來說,我覺得用這種方式似乎還有點把簡單功能複雜化了。
隻是,在網絡上我不止一次看到過它,似乎很多人都在用。因此我覺得還是了解一下、學習一下為好,或許它的好處隻是我暫時不清楚,而不代表這個好處不存在。
為了讓剛接觸的朋友能更好的理解,我這裏基本上都是代碼和測試都寫出來,並且三種方式簡單的對比,以便於在大家需要用到的時候能具體場景下具體的選擇。
實現方式一
完全的java代碼加注解的方式,首先創建一個簡單的帶有構造方法和get、set方法的類:
package springTest4;
public class ProFileTest {
private String msg;
public ProFileTest(String msg) {
super();
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void printTest() {
System.out.println(msg);
}
}
然後再創建一個相當於spring中xml配置文件的類,並且使用注解把這個類聲明成一個配置類,同時把上邊的類聲明為一個bean,並製定profile名稱(也就是上邊說的profile的選擇參數):
package springTest4;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class ProFileConf {
@Bean
@Profile("test1")
public ProFileTest proTest1() {
return new ProFileTest("test11111");
}
@Bean
@Profile("test2")
public ProFileTest proTest2() {
return new ProFileTest("test22222");
}
}
這裏的@Configuration即聲明這個類是一個相當於spring中xml的配置類,剩下兩個在前一段文字描述中已經說了,就不再贅述。
至於這段代碼的解釋,我想通過下邊調用的測試代碼之後再做解釋,模擬profile調用的main方法如下:
package springTest4;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainPro {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("test2");
context.register(ProFileConf.class);
context.refresh();
String mString = context.getBean(ProFileTest.class).getMsg();
System.out.println(mString);
context.close();
}
}
這裏的new AnnotationConfigApplicationContext()聲明一個空的上下文,我想學過spring的應該都不需要解釋。
context.getEnvironment().setActiveProfiles(“test2”)的意思是獲取環境變量,或者理解成獲取配置數據,然後把活動的profile的參數或者說名稱設置成test2。
context.register(ProFileConf.class)注冊具體的配置文件,這裏就是ProFileCon.class。
然後後邊的幾行代碼分別是刷新上下文,獲取具體的bean對象等等,這些就是比較常規的操作了。
上麵的方法執行後結果如圖:
而當我把context.getEnvironment().setActiveProfiles(“test2”)這裏的test2設置成test1的時候,結果就會成為test11111。
那麼結合上邊的ProFileConf 中的代碼,我想就比較容易理解了,也就是說這裏配置了profile參數後,當我們把activeProfiles設置成某個參數時,spring在加載時便會調用這個參數對應的profile的內容。
但是為什麼我要說看不出來這樣使用的好處在哪裏呢?是因為我覺得就這個例子來說,完全可以直接在創建新的對象時用構造方法的不同參數來實現。
當然了,這隻是個為了說明這項功能的例子,實際使用自然不會這樣,實際使用的時候,可能是用來加載不同的配置文件裏的值和屬性,也可能是用來調用不同的配置文件。
隻不過,即便是這樣,我依舊覺得也都可以用參數的方式實現,似乎沒有必要這樣繞來繞去,並沒看到怎麼簡化了開發,也沒看到有什麼性能上的提升。
那麼還有實現方式二
這個方式和上邊的其實是類似的,因為唯一的不同在於把起xml配置文件作用的java類實打實的變成xml配置,因為使用的有構造方法和get、set方法的類是同一個,因此就不再重複貼出來,這裏就從test1.xml開始:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<beans pro>
<bean >
<constructor-arg name="msg" value="test11111" />
</bean>
</beans>
<beans pro>
<bean >
<constructor-arg name="msg" value="test22222" />
</bean>
</beans>
</beans>
這也是一個極其簡單的spring配置,除開必要的文件頭尾,就隻有兩個幾乎一模一樣的beans,指定了profile名稱,以及內部的一個普通bean,這個bean以構造函數注入。
對於這種寫法,調用也基本是一樣的,隻不過context獲取的是xml而不是calss:
package springTest4;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainPro {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test1.xml");
context.getEnvironment().setActiveProfiles("test2");
context.refresh();
String mString = context.getBean(ProFileTest.class).getMsg();
System.out.println(mString);
context.close();
}
}
至於這裏的具體說明,我想通過對第一個例子的解釋,應該已經沒有必要再多說了。
實現方式三
前邊兩種方式,根本上來說是改變了profile的聲明方式,而這裏需要變得則是調用方式,前兩個拋開細節來說,都是在java類中調用,而這裏則是在web.xml中調用,當然了,既然有web.xml文件,自然也要是一個web項目才行,web.xml文件配置如下:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"https://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:test1.xml</param-value>
</init-param>
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>test2</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> >
</web-app>
這個web.xml也是一個隻為了說明profile的文件,隻要一個selvlet配置,兩個init-param,第一個是指定自定義的dispatcher-servlet.xml,如果不指定,啟動tomcat的時候就會出現如下的錯誤:
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/dispatcher-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:343)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
而第二個init-param則是指定默認的profile的名稱。
為了測試這種方式是否可行,我在第二種方式的基礎上略作了修改,java類中加入了一個init-method方法,修改之後對應的java類和xml配置如下:
package springTest4;
public class ProFileTest {
private String msg;
public ProFileTest(String msg) {
super();
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void printTest() {
System.out.println(msg);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<beans pro>
<bean init-method="printTest">
<constructor-arg name="msg" value="test11111" />
</bean>
</beans>
<beans pro>
<bean init-method="printTest">
<constructor-arg name="msg" value="test22222" />
</bean>
</beans>
</beans>
啟動tomcat的過程中,控製台也打印出了預想中的數據,證明第三種方式也是可行的。
以上便是我所知道的三種profile的實現方式,至於具體應用場景,我雖然知道主要是為了方便各種不同環境的切換。比如生產一套配置,測試一套配置;再比如一套單機環境,一套集群環境等等,但是我都覺得完全可以在部署的時候,在spring的context:property-placeholder以及import resource的時候切換。
而且這種切換比起profile來說似乎更加容易上手,所以就不太明白profile的優勢究竟在於哪裏,歡迎朋友們留言解惑。
最後更新:2017-09-11 18:03:21