5.3使用profile文件进行配置
当应用程序部署到不同的运行时环境时,通常会有一些配置细节不同。例如,数据库连接的细节在开发环境中可能与在QA环境中不一样,在生产环境中可能还不一样。在一个环境中唯一配置属性的一种方法是使用环境变量来指定配置属性,而不是在application.properties或application.yml中定义它们。
例如,在开发期间,可以依赖于自动配置的嵌入式H2数据库。但在生产中,可以将数据库配置属性设置为环境变量,如下所示:
% export SPRING_DATASOURCE_URL=jdbc:mysql://localhost/tacocloud
% export SPRING_DATASOURCE_USERNAME=tacouser
% export SPRING_DATASOURCE_PASSWORD=tacopassword
尽管这样做是可行的,但是将一两个以上的配置属性指定为环境变量就会变得有点麻烦。此外,没有跟踪环境变量更改的好方法,也没有在出现错误时轻松回滚更改的好方法。
相反,我更喜欢利用Spring profile文件。profile文件是一种条件配置类型,其中根据运行时激活的profile文件应用或忽略不同的bean、配置类和配置属性。
例如,假设出于开发和调试的目的,希望使用嵌入式H2数据库,并且希望将Taco Cloud代码的日志级别设置为DEBUG。但是在生产中,需要使用一个外部MySQL数据库,并将日志记录级别设置为WARN。在开发环境中,很容易不设置任何数据源属性并获得自动配置的H2数据库。至于DEBUG级别的日志记录,可以在application.yml中设置logging.level.tacos属性。
logging:
level:
tacos: DEBUG
这正是开发目的所需要的。但是,如果要将此应用程序部署到生产环境中,而不需要对application.yml进行进一步更改,仍然可以获得对于tacos包的调试日志和嵌入式H2数据库。需要的是定义一个具有适合生产的属性的profile文件。
5.3.1定义特定profile的属性
定义特定profile文件的属性的一种方法是创建另一个仅包含用于生产的属性的YAML或属性文件。文件的名称应该遵循这个约定:application-{profile名称}.yml或application-{profile名称}.properties。然后可以指定适合该配置文件的配置属性。例如,可以创建一个名为application-prod.yml的新文件,包含以下属性:
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword
logging:
level:
tacos: WARN
另一种指定特定profile文件的属性的方法只适用于YAML配置。它涉及在应用程序中将特定profile的属性与非profile的属性一起放在application.yml中,由三个连字符分隔。将生产属性应用于application.yml时,整个application.yml应该是这样的:
logging:
level:
tacos: DEBUG
---
spring:
profiles: prod
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword
logging:
level:
tacos: WARN
这个application.yml文件由一组三重连字符(—)分成两个部分。第二部分为spring.profiles指定一个值,这个值指示了随后应用于prod配置文件的属性。另一方面,第一部分没有为spring.profiles指定值。因此,它的属性对于所有profile文件都是通用的,或者如果指定的profile文件没有设置其他属性,它就是默认的。
无论应用程序运行时哪个配置文件处于活动状态,tacos包的日志级别都将通过默认配置文件中的属性设置为DEBUG。但是,如果名为prod的配置文件是活动的,那么logging.level.tacos属性将会被重写为WARN。同样,如果prod配置文件是活动的,那么数据源属性将设置为使用外部MySQL数据库。
通过创建使用application-{profile名称}.yml或application-{profile名称}.properties这种模式命名的其他YAML或properties文件,可以为任意数量的profile文件定义属性。或者在application.yml中再输入三个破折号通过spring.profiles来指定配置文件名称。然后添加需要的所有特定profile文件的属性。
5.3.2激活profile文件
设置特定profile属性没有什么意思,除非这些profile处于活动状态。但是要如何激活一个profile文件呢?让一个profile文件处于激活状态需要做的只是将spring.profiles.active属性的值指定为需要激活的profile的名称。例如,可以像下面这样设置application.yml中的这个属性:
spring:
profiles:
active:
- prod
但是这可能是设定一个活动profile最糟糕的方式了。如果在application.yml中设置了激活的profile,然后那个profile文件就变成了默认profile文件,那么就没有达到生产环境特定属性与开发环境特定属性分离的目的。相反,我推荐使用环境变量设置激活的profile。在生产环境,像下面这样设置SPRING_PROFILES_ACTIVE:
% export SPRING_PROFILES_ACTIVE=prod
这样设置完成后,部署于那台机器的任何应用程序将会使用prod profile,同时相应的配置属性将优先于默认配置文件中的属性。
如果使用可执行的JAR文件来运行应用程序,你可能也可以通过命令行设置激活的profile文件,如下所示:
% java -jar taco-cloud.jar --spring.profiles.active=prod
请注意spring.profiles.active属性名包含的是复数单词profiles。这意味着可以指定多个活动profiles文件。通常,这是一个逗号分隔的列表,当它设置一个环境变量:
% export SPRING_PROFILES_ACTIVE=prod,audit,ha
但是在YAML中,需要像下面这样指定它:
spring:
profiles:
active:
- prod
- audit
- ha
同样值得注意的是,如果将Spring应用程序部署到Cloud Foundry中,一个名为cloud的配置文件会自动激活。如果Cloud Foundry是生产环境,那么需要确保在cloud profile文件中指定了特定于生产环境的属性。
事实证明,配置文件只对在Spring应用程序中有条件地设置配置属性有用。让我们看看如何声明特定活动profile文件的bean。
5.3.3有条件地使用profile文件创建bean
有时候,为不同的配置文件提供一组惟一的bean是很有用的。通常,不管哪个profile文件是活动的,Java配置类中声明的任何bean都会被创建。但是,假设只有在某个配置文件处于活动状态时才需要创建一些bean,在这种情况下,@Profile注解可以将bean指定为只适用于给定的profile文件。
例如,在TacoCloudApplication中声明了一个CommandLineRunner bean,用于在应用程序启动时加载嵌入式数据库中的成分数据。这对于开发来说很好,但是在生产应用程序中是不必要的(也是不受欢迎的)。为了防止在每次应用程序在生产部署中启动时加载成分数据,可以使用@Profile像下面这样注解CommandLineRunner bean方法:
@Bean
@Profile("dev")
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
...
}
或是假设需要在dev profile或是qa profile被激活时创建CommandLineRunner,在这种情况下,可以列出需要的profile:
@Bean
@Profile({"dev", "qa"})
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
...
}
这样成分数据只会在dev或是qa profile文件被激活时才会被加载。这就意味着需要在开发环境运行应用程序时,将dev profile激活。如果这个CommandLineRunner bean总是被创建,除非prod配置文件是活动的,那就更方便了。在这种情况下,你可以像这样应用@Profile:
@Bean
@Profile("!prod")
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder){
...
}
在这里,感叹号 !
否定了配置文件名称。实际上,它声明如果prod配置文件不是活动的,就会创建CommandLineRunner bean。
也可以在整个@Configuration注解的类上使用@Profile。例如,假设要将CommandLineRunner bean提取到一个名为DevelopmentConfig的单独配置类中。然后你可以用@Profile来注解DevelopmentConfig:
@Profile({"!prod", "!qa"})
@Configuration
public class DevelopmentConfig {
@Bean
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder){
...
}
}
在这里,CommandLineRunner bean(以及在DevelopmentConfig中定义的任何其他bean)仅在prod和qa配置文件都不活动的情况下才会被创建。