在开发软件的时候,有一个很大的挑战就是将应用程序从一个环境迁移到另外一个环境。开发阶段中,某些环境相关做法可能并不适合迁移到
生产环境中,甚至即便迁移过去也无法正常工作。数据库配置、加密算法以及与外部系统的集成是跨环境部署时会发生变化的几个典型例子。
目录
配置profile bean
Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有太大的差别。当然,在这个过程中需要根据环境决定该创建哪个bean和不创建哪个bean。不过Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定。这样的结果就是同一个部署单元(可能会是 WAR文件)能够适用于所有的环境,没有必要进行重新构建。
在 Jave bean 配置
在 java 配置中,可以使用@Profile 注解指定某个 bean 属于某一个 prifile。比如 在h2 数据库的 数据源配置如下:
@Configuration
@Profile("dev")
public class DataSourceConfig {
@Bean(destroyMethod = "shutdown")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
}
此时的@Profile 注解是在类上面,这里的含义就是告诉这个配置类 只有在 dev 的时候,也就是开发环境的时候,profile 激活时才会创建。 如果没有激活,下面的 @Bean 注解将无任何作用。
类似的可以 在 @Profile 上面 写 prod 等。
在Spring 3.1中,只能在类级别上使用@Profile注解。
从Spring 3.2开始,你也可以在方法级别上使用@Profile注解,与@Bean注解一同使用。
这样的话,就能将这两个bean的声明放到同一个配置类之中:
@Profile注解基于激活的profile实现bean的装配
package com.myapp;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;
@Configuration
public class DataSourceConfig {
@Bean(destroyMethod = "shutdown")
@Profile("dev")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
@Bean
@Profile("prod")
public DataSource jndiDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
需要注意的是:没有指定profile的bean始终都会被创建,与激活哪个profile没有关系。
在 XML中配置profile
同样在 xml 中,可以采用 <bean> 元素的形式来 配置 prolife 属性。只是从 java 代码,改为 xml 配置,用法一样:
<beans profile="dev">
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:test-data.sql" />
</jdbc:embedded-database>
</beans>
将profile设置为prod:
<beans profile="prod">
<jee:jndi-lookup id="dataSource"
lazy-init="true"
jndi-name="jdbc/myDatabase"
resource-ref="true"
proxy-interface="javax.sql.DataSource" />
</beans>
激活profile
Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:
spring.profiles.active和spring.profiles.default。
- 如果设置了spring.profiles.active属性的话,那么它的值就会用来确定哪个profile是激活的。
- 如果没有设置spring.profiles.active属性的话,那Spring将会查找spring.profiles.default的值。
- 如果spring.profiles.active和spring.profiles.default均没有设置的话,那就没有激活的profile,因此只会创建那些没有定义在 profile中的bean。
有多种方式来设置这两个属性:
- 作为DispatcherServlet的初始化参数;
- 作为Web应用的上下文参数; 作为JNDI条目;
- 作为环境变量;
- 作为JVM的系统属性;
- 在集成测试类上,使用@ActiveProfiles注解设置。
按照这种方式设置spring.profiles.default,所有的开发人员都能从版本控制软件中获得应用程序源码,并使用开发环境的设置(如 嵌入式数据库)运行代码。
当应用程序部署到QA、生产或其他环境之中时,负责部署的人根据情况使用系统属性、环境变量或JNDI设置spring.profiles.active即可。当设置spring.profiles.active以后,至于spring.profiles.default置成什么值就已经无所谓了;系统会优先使 用spring.profiles.active中所设置的profile。
使用profile进行测试
当运行单元测试时,通常会希望采用与生产环境相同的配置进行测试。
Spring提供了@ActiveProfiles注解,我们可以使用它来指定运行测试时要激活哪个profile。一般情况下,用到的是 dev 下的 profile:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
@ActiveProfiles("dev")
public static class DevDataSourceTest {
}
很简单,类上面加个 @ActiveProfiles 即可,指定为 dev。
在条件化创建bean方面,profile是不错的选择,但前提是 需要 基于 哪个 profile 处于激活状态下才能来完成。