在本教程中,我们将着重介绍 Spring 中的 Profiles。
Profiles是框架的核心功能——允许我们将 bean 映射到不同的Profile——例如,dev、test 和 prod。
然后我们可以在不同的环境中激活不同的Profiles以仅引导我们需要的 bean。
@Profile
让我们从简单开始,看看如何让一个 bean 属于一个特定的Profile。 我们使用 @Profile
注解—我们将 bean 映射到那个特定的Profile; 注解采用一个(或多个)Profile的名称。
考虑一个基本场景:我们有一个 bean,它应该只在开发期间处于活动状态,但不能部署在生产环境中。
我们用 @Profile(“dev”) 注解该 bean,它只会在开发期间出现在容器中。 在生产中,dev 根本不会激活:
@Component
@Profile("dev")
public class DevDatasourceConfig
作为快速旁注,Profile名称也可以使用 NOT 运算符作为前缀,例如 !dev
,以将它们从Profile中排除。
在示例中,仅当 dev Profile未激活时才激活该组件:
@Component
@Profile("!dev")
public class DevDatasourceConfig
Profiles can also be configured in XML. The tag has a profile attribute, which takes comma-separated values of the applicable profiles:
<beans profile="dev">
<bean id="devDatasourceConfig"
class="org.baeldung.profiles.DevDatasourceConfig" />
beans>
下一步是激活和设置Profiles,以便在容器中注册相应的 bean。
这可以通过多种方式完成,我们将在以下部分中进行探讨。
WebApplicationInitializer
接口以编程方式在 Web 应用程序中,WebApplicationInitializer 可用于以编程方式配置 ServletContext。
这也是一个非常方便的位置,可以通过编程方式设置我们的激活的profiles:
@Configuration
public class MyWebApplicationInitializer
implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter(
"spring.profiles.active", "dev");
}
}
ConfigurableEnvironment
以编程方式我们还可以直接在environment中设置profiles:
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");
web.xml
中的上下文参数同样,我们可以使用上下文参数在 Web 应用程序的 web.xml 文件中定义活动Profile:
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>/WEB-INF/app-config.xmlparam-value>
context-param>
<context-param>
<param-name>spring.profiles.activeparam-name>
<param-value>devparam-value>
context-param>
profile名称也可以通过 JVM 系统参数传入。 这些profile将在应用程序启动期间被激活:
-Dspring.profiles.active=dev
在 Unix 环境下,profiles 也可以通过环境变量激活:
export SPRING_PROFILES_ACTIVE=dev
//或者
export spring_profiles_active=dev
Spring Profile也可以通过 Maven Profile激活,方法是指定 spring.profiles.active
配置属性。
在每个 Maven Profile中,我们可以设置一个 spring.profiles.active 属性:
<profiles>
<profile>
<id>devid>
<activation>
<activeByDefault>trueactiveByDefault>
activation>
<properties>
<spring.profiles.active>devspring.profiles.active>
properties>
profile>
<profile>
<id>prodid>
<properties>
<spring.profiles.active>prodspring.profiles.active>
properties>
profile>
profiles>
它的值将用于替换 application.properties
文件中@spring.profiles.active@
的占位符 :
spring.profiles.active=@spring.profiles.active@
现在我们需要在 pom.xml 中启用资源过滤:
<build>
<resources>
<resource>
<filtering>truefiltering>
<directory>src/main/resourcesdirectory>
<includes>
<include>public/**include>
<include>static/**include>
<include>templates/**include>
<include>tpl/**include>
<include>application.*include>
<include>application-${spring.profiles.active}.*include>
<include>log*-${spring.profiles.active}.xmlinclude>
includes>
resource>
resources>
...
build>
并附加一个 -P 参数来切换将应用哪个 Maven Profile:
mvn clean package -Pprod
此命令将为 prod Profile打包应用程序。 它还会在应用程序运行时应用 spring.profiles.active 值为 prod。
@ActiveProfile
在测试中使用 @ActiveProfile 注解启用特定profile,测试可以很容易地指定哪些profile处于活动状态:
@ActiveProfiles("dev")
到目前为止,我们已经研究了多种激活Profile的方法。 现在让我们看看哪个优先级高于另一个,如果我们使用多个优先级从高到低会发生什么:
任何未指定profile的 bean 都属于 default profile。
Spring 还提供了一种在没有其他profile处于活动状态时设置默认profile的方法—通过使用 spring.profiles.default 属性。
Spring 的活动Profile驱动 @Profile 注解的行为以启用/禁用 beans。 但是,我们也可能希望以编程方式访问活动profile列表。
我们有两种方法,**使用 Environment
或 spring.profiles.active
**。
我们可以通过注入 Environment 对象访问活动profiles:
public class ProfileManager {
@Autowired
private Environment environment;
public void getActiveProfiles() {
for (String profileName : environment.getActiveProfiles()) {
System.out.println("Currently active profile - " + profileName);
}
}
}
spring.profiles.active
或者,我们可以通过注入属性 spring.profiles.active 来访问profile:
@Value("${spring.profiles.active}")
private String activeProfile;
在这里,我们的 activeProfile 变量将包含当前处于活动状态的profile的名称,如果有多个,它将包含以逗号分隔的名称。
但是,我们应该**考虑如果根本没有活动profile会发生什么情况。**对于我们上面的代码,缺少profile将阻止创建应用程序上下文。 由于缺少用于注入变量的占位符,这将导致抛出 IllegalArgumentException。
为了避免这种情况,我们可以定义一个默认值:
@Value("${spring.profiles.active:}")
private String activeProfile;
现在,如果没有Profile处于活动状态,我们的 activeProfile 将只包含一个空字符串。
如果我们想像前面的示例一样访问它们的列表,我们可以通过拆分 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);
}
}
}
既然已经了解了基础知识,让我们来看一个真实的例子。
考虑这样一个场景,我们必须为开发和生产环境维护数据源配置。
让我们创建一个公共接口 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.
Spring Boot 支持到目前为止概述的所有profile配置,并具有一些附加功能。
第 4 节中介绍的初始化参数 spring.profiles.active 也可以设置为 Spring Boot 中的一个属性来定义当前活动的profiles。 这是 Spring Boot 将自动获取的标准属性:
spring.profiles.active=dev
但是,从 Spring Boot 2.4 开始,此属性不能与 spring.config.activate.on-profile 一起使用,因为这可能会引发 ConfigDataException(即 InvalidConfigDataPropertyException 或 InactiveConfigDataAccessException)。
要以编程方式设置profiles,我们还可以使用 SpringApplication 类:
SpringApplication.setAdditionalProfiles("dev");
要在 Spring Boot 中使用 Maven 设置Profile,我们可以在 pom.xml 中的 spring-boot-maven-plugin 下指定Profile名称:
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<profiles>
<profile>devprofile>
profiles>
configuration>
plugin>
...
plugins>
并执行特定于 Spring Boot 的 Maven 目标:
mvn spring-boot:run
然而,Spring Boot 带来的最重要的Profile相关功能是**特定于Profile的属性文件。**这些文件必须以 application-{profile}.properties 格式命名。
Spring Boot 将自动为所有profiles加载 application.properties 文件中的属性,并仅为指定的profile加载特定于profiles的 .properties 文件中的属性。
例如,我们可以使用名为 application-dev.properties 和 application-production.properties 的两个文件为 dev 和 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。
为了进一步简化为单独环境定义属性,我们甚至可以将所有属性组合在同一个文件中,并使用分隔符来指示Profile。
从 2.4 版开始,Spring Boot 除了以前支持的 YAML 之外,还扩展了对属性文件的多文档文件的支持。 所以现在,我们可以在同一个 *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,在上面的示例中再次出现在末尾,则将考虑最末端的值。
Spring Boot 2.4 中添加的另一个功能是配置Profile组。 顾名思义,它允许我们将相似的Profile分组在一起。
让我们考虑一个用例,其中我们有多个生产环境的profiles。 比如,proddb 用于数据库,prodquartz 用于 production 环境中的调度程序。
要通过我们的 application.properties 文件同时启用这些Profile,我们可以指定:
spring.profiles.group.production=proddb,prodquartz
因此,激活 production Profile也会激活 proddb 和 prodquartz。
<<<<<< [完] >>>>>>