共计 3743 个字符,预计需要花费 10 分钟才能阅读完成。
Profile 本身是 Spring 提供的功能,我们在使用条件装配中已经讲到了,Profile 表示一个环境的概念,如开发、测试和生产这 3 个环境:
- native
- test
- production
或者按 git 分支定义 master、dev 这些环境:
- master
- dev
在启动一个 Spring 应用程序的时候,可以传入一个或多个环境,例如:
-Dspring.profiles.active=test,master
大多数情况下,使用一个环境就足够了。
Spring Boot 对 Profiles 的支持在于,可以在 application.yml
中为每个环境进行配置。下面是一个示例配置:
spring: | |
application: | |
name: ${APP_NAME:unnamed} | |
datasource: | |
url: jdbc:hsqldb:file:testdb | |
username: sa | |
password: | |
dirver-class-name: org.hsqldb.jdbc.JDBCDriver | |
hikari: | |
auto-commit: false | |
connection-timeout: 3000 | |
validation-timeout: 3000 | |
max-lifetime: 60000 | |
maximum-pool-size: 20 | |
minimum-idle: 1 | |
pebble: | |
suffix: | |
cache: false | |
server: | |
port: ${APP_PORT:8080} | |
spring: | |
config: | |
activate: | |
on-profile: test | |
server: | |
port: 8000 | |
spring: | |
config: | |
activate: | |
on-profile: production | |
server: | |
port: 80 | |
pebble: | |
cache: true |
注意到分隔符 ---
,最前面的配置是默认配置,不需要指定 Profile,后面的每段配置都必须以spring.config.activate.on-profile: xxx
开头,表示一个 Profile。上述配置默认使用 8080 端口,但是在 test
环境下,使用 8000
端口,在 production
环境下,使用 80
端口,并且启用 Pebble 的缓存。
如果我们不指定任何 Profile,直接启动应用程序,那么 Profile 实际上就是default
,可以从 Spring Boot 启动日志看出:
... | |
2022-11-25T11:10:34.006+08:00 INFO 13537 --- [main] com.itranswarp.learnjava.Application : No active profile set, falling back to 1 default profile: "default" |
上述日志显示未设置 Profile,使用默认的 Profile 为default
。
要以 test
环境启动,可输入如下命令:
$ java -Dspring.profiles.active=test -jar springboot-profiles-1.0-SNAPSHOT.jar | |
2022-11-25T11:09:02.946+08:00 INFO 13510 --- [main] com.itranswarp.learnjava.Application : The following 1 profile is active: "test" | |
2022-11-25T11:09:05.124+08:00 INFO 13510 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8000 (http) with context path '' | |
从日志看到活动的 Profile 是test
,Tomcat 的监听端口是8000
。
通过 Profile 可以实现一套代码在不同环境启用不同的配置和功能。假设我们需要一个存储服务,在本地开发时,直接使用文件存储即可,但是,在测试和生产环境,需要存储到云端如 S3 上,如何通过 Profile 实现该功能?
首先,我们要定义存储接口StorageService
:
public interface StorageService {// 根据 URI 打开 InputStream: | |
InputStream openInputStream(String uri) throws IOException; | |
// 根据扩展名 +InputStream 保存并返回 URI: | |
String store(String extName, InputStream input) throws IOException; | |
} |
本地存储可通过 LocalStorageService
实现:
public class LocalStorageService implements StorageService { | |
String localStorageRootDir; | |
final Logger logger = LoggerFactory.getLogger(getClass()); | |
private File localStorageRoot; | |
public void init() {logger.info("Intializing local storage with root dir: {}", this.localStorageRootDir); | |
this.localStorageRoot = new File(this.localStorageRootDir); | |
} | |
public InputStream openInputStream(String uri) throws IOException {File targetFile = new File(this.localStorageRoot, uri); | |
return new BufferedInputStream(new FileInputStream(targetFile)); | |
} | |
public String store(String extName, InputStream input) throws IOException {String fileName = UUID.randomUUID().toString() + "." + extName; | |
File targetFile = new File(this.localStorageRoot, fileName); | |
try (OutputStream output = new BufferedOutputStream(new FileOutputStream(targetFile))) {input.transferTo(output); | |
} | |
return fileName; | |
} | |
} |
而云端存储可通过 CloudStorageService
实现:
"!default") | (|
public class CloudStorageService implements StorageService { ("${storage.cloud.bucket:}") | |
String bucket; | |
"${storage.cloud.access-key:}") | (|
String accessKey; | |
"${storage.cloud.access-secret:}") | (|
String accessSecret; | |
final Logger logger = LoggerFactory.getLogger(getClass()); | |
public void init() {// TODO: | |
logger.info("Initializing cloud storage..."); | |
} | |
public InputStream openInputStream(String uri) throws IOException {// TODO: | |
throw new IOException("File not found:" + uri); | |
} | |
public String store(String extName, InputStream input) throws IOException {// TODO: | |
throw new IOException("Unable to access cloud storage."); | |
} | |
} |
注意到 LocalStorageService
使用了条件装配 @Profile("default")
,即默认启用LocalStorageService
,而CloudStorageService
使用了条件装配 @Profile("!default")
,即非default
环境时,自动启用CloudStorageService
。这样,一套代码,就实现了不同环境启用不同的配置。
练习
使用 Profile 启动 Spring Boot 应用。
下载练习
小结
Spring Boot 允许在一个配置文件中针对不同 Profile 进行配置;
Spring Boot 在未指定 Profile 时默认为default
。
