Spring Profile 指南
1、概览
本文将带你了解 Spring 中的 Profile(配置文件),这是 Spring 的核心功能之一。可以把 Bean 配置在不同的 Profile,例如:dev、test、prod。然后,可以在不同的环境中激活指定的 Profile,以便只加载当前环境所需的 Bean。
2、在 Bean 上使用 @Profile
从简单的开始,看看如何使用 @Profile
注解将 Bean 映射到特定的 Profile。
该注解接受一个(或多个) Profile 名称。
考虑一个场景:有一个 Bean,它只能在开发过程中激活,不能在生产过程中部署。
用 dev Profile 注解该 Bean,它只会在开发环境中被加载到容器。在生产环境中, dev Profile 不会被激活:
@Component
@Profile("dev")
public class DevDatasourceConfig
Profile 名称也可以用 NOT 运算符作为前缀,如 !dev,以将其从 Profile 排除。
在如下示例中,只有当 dev Profile 未激活时,组件才会被激活:
@Component
@Profile("!dev")
public class DevDatasourceConfig
3、在 XML 中声明 Profile
Profile 也可以用 XML 配置。<beans>
标签有一个 profile
属性,该属性包含以逗号分隔的 Profile 值:
<beans profile="dev">
<bean id="devDatasourceConfig"
class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>
4、设置 Profile
下一步是激活和设置 Profile,以便在容器中注册相应的 Bean。
4.1、WebApplicationInitializer
在 Web 应用程序中,WebApplicationInitializer
可用于以编程式配置 ServletContext
。
这也是一个非常方便的位置,可以通过编程式设置激活的 Profile:
@Configuration
public class MyWebApplicationInitializer
implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter(
"spring.profiles.active", "dev");
}
}
4.2、ConfigurableEnvironment
还可以直接在环境中设置 Profile:
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");
4.3、web.xml 中的 Context Parameter
同样,也可以使用 Context Parameter 在 Web 应用的 web.xml
文件中定义激活的 Profile:
<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.4、JVM 系统参数
Profile 名称也可以通过 JVM 系统参数传递。这些 Profile 将在应用启动时激活:
-Dspring.profiles.active=dev
4.5、环境变量
在 Unix 环境中,也可以通过环境变量激活 Profile:
export spring_profiles_active=dev
4.6、Maven Profile
Spring Profile 也可以通过在 Maven Profile 中指定 spring.profiles.active
配置属性来激活。
在每个 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.profiles.active@
占位符:
spring.profiles.active=@spring.profiles.active@
现在,需要在 pom.xml
中启用资源过滤功能:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
...
</build>
并附加一个 -P
参数,以切换要应用的 Maven Profile:
mvn clean package -Pprod
该命令将为 prod Profile 打包应用。在应用运行时,它还会应用 spring.profiles.active
的值 prod。
4.7、测试中的 @ActiveProfile
使用 @ActiveProfiles
注解可以很容易地指定激活哪些 Profile,从而使测试变得非常简单:
@ActiveProfiles("dev")
到目前为止,我们已经了解了激活 Profile 的多种方法。
web.xml
中的 Context parameterWebApplicationInitializer
- JVM 系统参数
- 环境变量
- Maven Profile
思考:但是每种方式的优先级是什么?如果从高到低使用多个 Profile 会发生什么情况?
5、Default Profile
任何未指定 Profile 的 Bean 都属于 default Profile。
当没有其他 Profile 处于活动状态时,Spring 还提供了一种设置默认 Profile 的方法 - 使用 spring.profiles.default
属性。
6、获取激活的 Profile
有时,我们可能需要在运行时获取到激活的 Profile 列表。
有两种方法:使用 Environment
接口或 spring.profiles.active
属性。
6.1、使用 Environment
可以通过注入 Environment
对象来访问激活的 Profile:
public class ProfileManager {
@Autowired
private Environment environment;
public void getActiveProfiles() {
for (String profileName : environment.getActiveProfiles()) {
System.out.println("Currently active profile - " + profileName);
}
}
}
6.2、使用 spring.profiles.active
或者,也可以通过注入 spring.profiles.active
属性来访问激活的 Profile:
@Value("${spring.profiles.active}")
private String activeProfile;
如上,activeProfile
变量包含当前处于活动状态的 Profile 的名称,如果有多个 Profile,则包含用逗号分隔的名称。
不过,如果没有激活的 Profile,就无法创建 Application Context。由于缺少注入变量的占位符,这会抛出 IllegalArgumentException
异常。
为了避免这种情况,可以定义一个默认值:
@Value("${spring.profiles.active:}")
private String activeProfile;
现在,如果没有 Profile 处于活动状态,activeProfile
会是一个空字符串。
如果想像上一个示例那样以 List
形式访问 Profile,可以通过切割 activeProfile
变量来实现:
public class ProfileManager {
@Value("${spring.profiles.active:}")
private String activeProfiles;
public String getActiveProfiles() {
for (String profileName : activeProfiles.split(",")) {
System.out.println("Currently active profile - " + profileName);
}
}
}
7、示例: 使用 Profile 分离数据源配置
基础知识讲完了,来看一个真实的例子。
假设我们需要同时维护开发环境和生产环境的数据源配置。
创建一个通用接口 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,Spring 将注入 DevDatasourceConfig
或 ProductionDatasourceConfig
Bean:
public class SpringProfilesWithMavenPropertiesIntegrationTest {
@Autowired
DatasourceConfig datasourceConfig;
public void setupDatasource() {
datasourceConfig.setup();
}
}
当 dev Profile 处于活动状态时,Spring 会注入 DevDatasourceConfig
对象,当调用 setup()
方法时,输出如下:
Setting up datasource for DEV environment.
8、Spring Boot 中的 Profile
Spring Boot 支持迄今为止概述的所有 Profile 配置,并具有一些额外功能。
8.1、激活或设置 Profile
在第 4 节中介绍的初始化参数 spring.profiles.active
也可以在 Spring Boot 中设置为属性,用于定义当前激活 Profile。这是一个标准属性,Spring Boot 会自动获取它:
spring.profiles.active=dev
但是,从 Spring Boot 2.4 开始,该属性不能与 spring.config.activate.on-profile
结合使用,否则会引发 ConfigDataException
(即 InvalidConfigDataPropertyException
或 InactiveConfigDataAccessException
)。
要以编程式设置 Profile,还可以使用 SpringApplication
类:
SpringApplication.setAdditionalProfiles("dev");
要在 Spring Boot 中使用 Maven 设置 Profile,可以在 pom.xml
中的 spring-boot-maven-plugin
下指定 Profile 名称:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<profiles>
<profile>dev</profile>
</profiles>
</configuration>
</plugin>
...
</plugins>
并执行 Spring Boot 专用的 Maven goal:
mvn spring-boot:run
8.2、特定于 Profile 的 Properties 文件
不过,Spring Boot 带来的与 Profile 相关的最重要功能是特定于 Profile 的 Properties 文件。这些文件必须以 application-{profile}.properties
格式命名。
Spring Boot 会为所有 Profile 自动加载 application.properties
文件中的属性,而仅为指定 Profile 自动加载特定 Profile .properties 文件中的属性。
例如,可以使用名为 application-dev.properties
和 application-production.properties
的两个文件,为 dev
Profile 和 production
Profile 配置不同的数据源:
在 application-production.properties
文件中,可以设置 MySql 数据源:
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
文件中为 dev Profile 配置相同的属性,但是数据源使用的是 H2 内存数据库:
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
这样,就可以轻松地为不同的环境提供不同的配置。
在 Spring Boot 2.4 之前,可以通过特定 Profile 的文档激活 Profile。但现在已不再支持;在以后的版本中,框架会在这种情况下抛出 InvalidConfigDataPropertyException
或 InactiveConfigDataAccessException
异常。
8.3、多文档文件
为了进一步简化为不同环境定义属性的过程,甚至可以将所有属性放在同一个文件中,并使用分隔符来表示 Profile。
从 2.4 版开始,除了之前支持的 YAML 之外,Spring Boot 还扩展了对 properties 文件的多文档文件支持。因此,现在可以在同一个 application.properties
中指定 dev 和 production 属性:
my.prop=used-always-in-all-profiles
#---
spring.config.activate.on-profile=dev
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
#---
spring.config.activate.on-profile=production
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
Spring Boot 会按从上到下的顺序读取该文件。也就是说,如果在上面的示例中,某个属性(例如 my.prop
)在最后出现了多次,那么最后一次出现的值将被视为有效值。
8.4、Profile 组
Spring Boot 2.4 中添加的另一项功能是 “Profile Group”。顾名思义,它允许将类似的 Profile 分组。
再考虑一个使用案例,在生产环境中有多个 Profile。例如,在 production
环境中,可能有一个用于数据库的 proddb
Profile,以及一个用于调度器的 prodquartz
Profile。
要通过 application.properties
文件一次性启用这些 Profile,可以指定:
spring.profiles.group.production=proddb,prodquartz
因此,激活 production
Profile 也会激活 proddb
和 prodquartz
。
9、总结
本文介绍了 Spring Profile 的特性、应用场景,以及如何在 Spring 和 Spring Boot 中定义、使用 Profile。
Ref:https://www.baeldung.com/spring-profiles