1. 概述
这篇文章将阐述怎么在 Spring 中使用 Profile
从 Spring 3.1 开始,我们能够将 bean 映射到不同的 profile 上,如 dev, test, prod 等。
我们也能够根据环境 (environment) 来激活不同的 profile,从而加载我们需要的 bean。
2. 在 Bean 上使用 @Profile
我们先从简单的例子开始,看看怎么把 bean 绑定到不同的 profile 上。
使用 @Profile 注解,我们可以将 bean 绑定到指定的 profile 上。这个注解支持绑定一个或多个 profile。
试想这样一个场景:我们有一个 bean,只在开发环境需要,线上环境不需要。那么我们可以通过注解将这个 bean 绑定到 dev profile 上。这样,这个 bean 只会存在于开发环境,而在其他环境中不会被加载。如下所示:
@Component
@Profile("dev")
public class DevDatasourceConfig
上面的写法是指绑定 bean 到 dev profile。如果想绑定 bean 到除 dev 以外的 profile 呢。可以使用 NOT 操作符。如下所示:
@Component
@Profile("!dev")
public class DevDatasourceConfig
译者注:
@Profile的声明如下:public @interface Profile { String[] value(); }
value是个数组,支持多个值。绑定多个 profile,如下所示:@Profile(value = {"dev", "test"})
3. 在 XML 中声明 Profile
除了使用 @Profile 注解,还可以在 XML 中绑定 profile。
<bean> 标签有个 profiles 属性。多个 profile 之间使用逗号分隔:
profile还是profiles???
<beans profile="dev">
<bean id="devDatasourceConfig"
class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>
4. 设置 profile
上面只是将 bean 和 profile 进行了绑定,下一步需要设置和激活 profile,才能使不同的 bean 被注册到容器中。方法有很多种,下面是一些例子:
4.1 使用 WebApplicationInitializer interface
这是一种编程的方式 (与之对应是配置方式)
在 web 应用中,WebApplicationInitializer 可以被用来配置 ServletContext。
译者注:
WebApplicationInitializer在 spring-web 中
方法如下:
@Configuration
public class MyWebApplicationInitializer
implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter(
"spring.profiles.active", "dev");
}
}
译者注:
这种方式,直接把要绑定的 profile 硬编码到代码中,是非常不优雅,也不方便的。
4.2 使用 ConfigurableEnvironment
你也可以直接在环境中设置 profile:
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");
译者注:
问题同 4.1
4.3 在 web.xml 中配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
译者注:
问题同 4.1,4.2。 本人不赞成任何硬编码的方式
4.4 通过 JVM 参数设置
profile 的名字还可以通过 JVM 参数的方式设置。在启动的时候,添加类似如下参数:
-Dspring.profiles.active=dev
译者注:
如java -jar xxx.jar -Dspring.profiles.active=dev
4.5 通过环境变量设置
通过设置环境变量,也是可以的:
export spring_profiles_active=dev
4.6 Maven Profile
Spring profile 也可以结合 maven profile 使用,通过设置 spring.profiles.active 属性:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.profiles.active>dev</spring.profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
</profile>
</profiles>
同时,还需要在 application.properties(如果使用 spring boot 的话) 中添加如下设置:
spring.profiles.active=@spring.profiles.active@
另外,还需要再 pom.xml 文件中添加如下配置:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
...
</build>
译者注:
在pom.xml中添加的配置,意思是开启占位符替换。可参考:Maven Filtering
结合使用 maven profile 后,就可以在打包的时候激活某个 profile:
mvn clean package -Pprod
译者注:
个人认为,结合 maven profile 的这种办法,灵活度最高,也最方便,推荐。
4.7 Test 中使用 @ActiveProfiles
在 test 时,如何指定 profile 呢?也很简单,通过 @ActiveProfiles 注解就可以了。
@ActiveProfiles(“dev”)
5. 默认 profile
如果不指定任何 profile,那么这个 bean 就属于 default profile
当没有任何 profile 被激活时,spring 也支持设置默认 profile。就是通过 spring.profiles.default 参数。
6. 获取被激活的 profile
一旦 profile 被激活,我们就可以通过 Environment,在运行时获取这些 profile 的信息:
public class ProfileManager {
@Autowired
Environment environment;
public void getActiveProfiles() {
for (final String profileName : environment.getActiveProfiles()) {
System.out.println("Currently active profile - " + profileName);
}
}
}
7. 使用 Profile 的例子
理论总是抽象的,下面我们通过一些例子来深入理解。
试想这样一个场景:我们要分别针对开发环境和线上环境,对 datasource 进行设置。我们先创建一个 DatasourceConfig 接口。这个接口需要在两个环境中都被实现。
public interface DatasourceConfig {
public void setup();
}
开发环境的实现:
@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {
@Override
public void setup() {
System.out.println("Setting up datasource for DEV environment. ");
}
}
线上环境的实现:
@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
@Override
public void setup() {
System.out.println("Setting up datasource for PRODUCTION environment. ");
}
}
下面我们写个单元测试,并注入 DatasourceConfig。那么我们通过设置不同的 profile,就会分别注入 DevDatasourceConfig bean 和 ProductionDatasourceConfig bean。
public class SpringProfilesTest {
@Autowired
DatasourceConfig datasourceConfig;
public void setupDatasource() {
datasourceConfig.setup();
}
}
当 dev profile 被激活的时候,会有如下输出:
Setting up datasource for DEV environment.
8. 在 Spring Boot 中使用 Profile
Spring Boot 除了支持所有的 profile 配置,还有提供一些额外功能。
spring.profiles.active 的初始化可以在配置文件中设定:
spring.profiles.active=dev
当然也可以在程序中设定:
SpringApplication.setAdditionalProfiles("dev");
还可以在 pom.xml 文件中的 spring-boot-maven-plugin 中配置:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<profiles>
<profile>dev</profile>
</profiles>
</configuration>
</plugin>
...
</plugins>
用 maven 启动,可执行命令:
mvn spring-boot:run
但是 Spring Boot 带来的最重要的特性是 profile-specific profiles 文件,这些文件的命名方式需要遵循 applications-{profile}.properties 的格式。
举个例子:我们可以在开发环境和线上环境使用不同的数据库。如开发环境使用 h2,线上环境使用 mysql。那么,我们分别需要创建如下两个文件:
application-production.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root
application-dev.properties
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
如果激活的 profile 是 dev,则 application-dev.properties 配置文件会被自动加载。如果激活的 profile 是 production,则 application-production.properties 配置文件会被加载。
通过这种方式,我们就很容易地针对不同环境,配置不同的配置文件。
