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");
- }
- }
中 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="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">
- <beans profile="test1">
- <bean id="proTest1" class="springTest4.ProFileTest">
- <constructor-arg name="msg" value="test11111" />
- </bean>
- </beans>
- <beans profile="test2">
- <bean id="proTest2" class="springTest4.ProFileTest">
- <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" "http://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="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">
- <beans profile="test1">
- <bean id="proTest1" class="springTest4.ProFileTest" init-method="printTest">
- <constructor-arg name="msg" value="test11111" />
- </bean>
- </beans>
- <beans profile="test2">
- <bean id="proTest2" class="springTest4.ProFileTest" init-method="printTest">
- <constructor-arg name="msg" value="test22222" />
- </bean>
- </beans>
- </beans>
启动 tomcat 的过程中,控制台也打印出了预想中的数据,证明第三种方式也是可行的。
以上便是我所知道的三种 profile 的实现方式,至于具体应用场景,我虽然知道主要是为了方便各种不同环境的切换。比如生产一套配置,测试一套配置;再比如一套单机环境,一套集群环境等等,但是我都觉得完全可以在部署的时候,在 spring 的 context:property-placeholder 以及 import resource 的时候切换。
而且这种切换比起 profile 来说似乎更加容易上手,所以就不太明白 profile 的优势究竟在于哪里,欢迎朋友们留言解惑。
来源: http://www.bubuko.com/infodetail-1870110.html