Spring为应用程序准备了Profile这一概念,用来表示不同的环境。例如,我们分别定义开发、测试和生产这3个环境:
- native
- test
- production
创建某个Bean时,Spring容器可以根据注解来决定是否创建。例如,以下配置:
如果当前的Profile设置为test
,则Spring容器会调用createZoneIdForTest()
创建ZoneId
,否则,调用createZoneId()
创建ZoneId
。注意到@Profile("!test")
表示非test环境。
在运行程序时,加上JVM参数-Dspring.profiles.active=test
就可以指定以test
环境启动。
实际上,Spring允许指定多个Profile,例如:
-Dspring.profiles.active=test,master
可以表示test
环境,并使用master
分支代码。
@Bean
@Profile({ "test", "master" }) // 同时满足test和master
...
}
除了根据@Profile
条件来决定是否创建某个Bean外,Spring还可以根据@Conditional
决定是否创建某个Bean。
例如,我们对SmtpMailService
添加如下注解:
它的意思是,如果满足的条件,才会创建SmtpMailService
这个Bean。OnSmtpEnvCondition
的条件是什么呢?我们看一下代码:
public class OnSmtpEnvCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return "true".equalsIgnoreCase(System.getenv("smtp"));
}
}
因此,OnSmtpEnvCondition
的条件是存在环境变量smtp
,值为true
。这样,我们就可以通过环境变量来控制是否创建SmtpMailService
。
Spring只提供了@Conditional
注解,具体判断逻辑还需要我们自己实现。Spring Boot提供了更多使用起来更简单的条件注解,例如,如果配置文件中存在app.smtp=true
,则创建MailService
:
@Component
@ConditionalOnProperty(name="app.smtp", havingValue="true")
public class MailService {
}
如果当前classpath中存在类,则创建MailService
:
@Component
@ConditionalOnProperty(name = "app.storage", havingValue = "file", matchIfMissing = true)
public class FileUploader implements Uploader {
...
}
在生产环境运行时,我们会把文件存储到类似AWS S3上:
@Component
@ConditionalOnProperty(name = "app.storage", havingValue = "s3")
public class S3Uploader implements Uploader {
...
其他需要存储的服务则注入Uploader
:
当应用程序检测到配置文件存在app.storage=s3
时,自动使用S3Uploader
,如果存在配置app.storage=file
,或者配置app.storage
不存在,则使用FileUploader
。
可见,使用条件注解,能更灵活地装配Bean。
从下载练习:使用@Profile进行条件装配 (推荐使用快速下载)
Spring允许通过@Profile
配置不同的Bean;