1. 概述
在本教程中,我们将着重介绍 Spring 中的 Profiles。
Profiles是框架的核心功能——允许我们将 bean 映射到不同的Profile——例如,dev、test 和 prod。
然后我们可以在不同的环境中激活不同的Profiles以仅引导我们需要的 bean。
2. 在 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
3. 在 XML 中声明Profiles
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>
4. 设置 Profiles
下一步是激活和设置Profiles,以便在容器中注册相应的 bean。
这可以通过多种方式完成,我们将在以下部分中进行探讨。
4.1. 通过 WebApplicationInitializer
接口以编程方式
在 Web 应用程序中,WebApplicationInitializer 可用于以编程方式配置 ServletContext。
这也是一个非常方便的位置,可以通过编程方式设置我们的激活的profiles:
@Configurationpublic class MyWebApplicationInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException { servletContext.setInitParameter("spring.profiles.active", "dev");}}
4.2. 通过 ConfigurableEnvironment
以编程方式
我们还可以直接在environment中设置profiles:
@Autowiredprivate ConfigurableEnvironment env;...env.setActiveProfiles("someProfile");
4.3. web.xml
中的上下文参数
同样,我们可以使用上下文参数在 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 环境下,profiles 也可以通过环境变量激活:
export SPRING_PROFILES_ACTIVE=dev//或者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><filtering>true</filtering><directory>src/main/resources</directory><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}.xml</include></includes></resource></resources>...</build>
并附加一个 -P 参数来切换将应用哪个 Maven Profile:
mvn clean package -Pprod
此命令将为 prod Profile打包应用程序。 它还会在应用程序运行时应用 spring.profiles.active 值为 prod。
4.7. @ActiveProfile
在测试中
使用 @ActiveProfile 注解启用特定profile,测试可以很容易地指定哪些profile处于活动状态:
@ActiveProfiles("dev")
到目前为止,我们已经研究了多种激活Profile的方法。 现在让我们看看哪个优先级高于另一个,如果我们使用多个优先级从高到低会发生什么:
- web.xml 文件里的上下文参数
- WebApplicationInitializer接口
- JVM 系统参数
- 环境变量
- Maven profile
5. 默认的 Profile
任何未指定profile的 bean 都属于 default profile。
Spring 还提供了一种在没有其他profile处于活动状态时设置默认profile的方法—通过使用 spring.profiles.default 属性。
6. 获取活动Profile
Spring 的活动Profile驱动 @Profile 注解的行为以启用/禁用 beans。 但是,我们也可能希望以编程方式访问活动profile列表。
我们有两种方法,**使用 Environment
或 spring.profiles.active
**。
6.1. 使用 Environment
我们可以通过注入 Environment 对象访问活动profiles:
public class ProfileManager {@Autowiredprivate 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将阻止创建应用程序上下文。 由于缺少用于注入变量的占位符,这将导致抛出 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);}}}
7. 示例:使用Profiles分离数据源配置
既然已经了解了基础知识,让我们来看一个真实的例子。
考虑这样一个场景,我们必须为开发和生产环境维护数据源配置。
让我们创建一个公共接口 DatasourceConfig ,它需要由两个数据源实现来实现:
public interface DatasourceConfig {public void setup();}
下面是开发环境的配置:
@Component@Profile("dev")public class DevDatasourceConfig implements DatasourceConfig {@Overridepublic void setup() {System.out.println("Setting up datasource for DEV environment. ");}}
以及生产环境的配置:
@Component@Profile("production")public class ProductionDatasourceConfig implements DatasourceConfig {@Overridepublic void setup() { System.out.println("Setting up datasource for PRODUCTION environment. ");}}
现在让我们创建一个测试并注入我们的 DatasourceConfig 接口; 根据活动profile,Spring 将注入 DevDatasourceConfig 或 ProductionDatasourceConfig bean:
public class SpringProfilesWithMavenPropertiesIntegrationTest {@AutowiredDatasourceConfig datasourceConfig;public void setupDatasource() {datasourceConfig.setup();}}
当 dev profile 处于活动状态时,Spring 注入 DevDatasourceConfig 对象,然后调用 setup() 方法时,输出如下:
Setting up datasource for DEV environment.
8. Spring Boot 中的Profiles
Spring Boot 支持到目前为止概述的所有profile配置,并具有一些附加功能。
8.1. 激活或设置一个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.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><profiles><profile>dev</profile></profiles></configuration></plugin>...</plugins>
并执行特定于 Spring Boot 的 Maven 目标:
mvn spring-boot:run
8.2.特定于Profile的属性文件
然而,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.Driverspring.datasource.url=jdbc:mysql://localhost:3306/dbspring.datasource.username=rootspring.datasource.password=root
然后我们可以在 application-dev.properties 文件中为 dev Profile配置相同的属性,以使用内存中的 H2 数据库:
spring.datasource.driver-class-name=org.h2.Driverspring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1spring.datasource.username=saspring.datasource.password=sa
这样我们就可以很方便的为不同的环境提供不同的配置。
在 Spring Boot 2.4 之前,可以从特定于Profile的文档中激活Profile。 但情况已不再如此; 对于更高版本,在这些情况下,框架将再次抛出 InvalidConfigDataPropertyException 或 InactiveConfigDataAccessException。
8.3. 多文档文件
为了进一步简化为单独环境定义属性,我们甚至可以将所有属性组合在同一个文件中,并使用分隔符来指示Profile。
从 2.4 版开始,Spring Boot 除了以前支持的 YAML 之外,还扩展了对属性文件的多文档文件的支持。 所以现在,我们可以在同一个 *application.properties* 中指定 dev
和 production
属性:
my.prop=used-always-in-all-profiles#---spring.config.activate.on-profile=devspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/dbspring.datasource.username=rootspring.datasource.password=root#---spring.config.activate.on-profile=productionspring.datasource.driver-class-name=org.h2.Driverspring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1spring.datasource.username=saspring.datasource.password=sa
该文件由 Spring Boot 按从上到下的顺序读取。 也就是说,如果某些属性,比如 my.prop,在上面的示例中再次出现在末尾,则将考虑最末端的值。
8.4. Profile 组
Spring Boot 2.4 中添加的另一个功能是配置Profile组。 顾名思义,它允许我们将相似的Profile分组在一起。
让我们考虑一个用例,其中我们有多个生产环境的profiles。 比如,proddb 用于数据库,prodquartz 用于 production 环境中的调度程序。
要通过我们的 application.properties 文件同时启用这些Profile,我们可以指定:
spring.profiles.group.production=proddb,prodquartz
因此,激活 production Profile也会激活 proddb 和 prodquartz。
<<<<<>>>>>