Spring @ConditionalOnProperty 注解

转自 https://www.baeldung.com/spring-conditionalonproperty

1. 概述

在这个简短的教程中,我们将阐明 @ConditionalOnProperty 注解的主要目的。

首先,我们将从一些关于@ConditionalOnProperty 是什么的背景开始。 然后,我们将看一些实际的例子来帮助理解它是如何工作的以及它带来了什么功能。

2. @ConditionalOnProperty 的目的

通常,在开发基于 Spring 的应用程序时,我们可能需要根据配置属性的存在和值有条件地创建一些 bean。

例如,我们可能想要注册一个 DataSource bean 以指向生产或测试数据库,这取决于我们将属性值设置为“prod”还是“test”。

幸运的是,实现这一目标并不难。 Spring 框架正是为此目的提供了 @ConditionalOnProperty 注解。

简而言之,@ConditionalOnProperty 仅在环境属性存在且具有特定值时才启用 bean 注册。 默认情况下,目标属性要被定义并且不等于false。

现在我们已经熟悉了 @ConditionalOnProperty 注解的用途,让我们深入了解它是如何工作的。

3. 实践中的 @ConditionalOnProperty 注解

为了举例说明@ConditionalOnProperty 的使用,我们将开发一个基本的通知系统。 现在为了简单起见,假设我们要发送电子邮件通知。

首先,我们需要创建一个简单的服务来发送通知消息。 例如,考虑 NotificationSender 接口:

1
2
3
public interface NotificationSender {
String send(String message);
}

接下来,让我们提供一个 NotificationSender 接口的实现来发送我们的电子邮件:

1
2
3
4
5
6
public class EmailNotification implements NotificationSender {
@Override
public String send(String message) {
return "Email Notification: " + message;
}
}

现在,让我们看看如何使用 @ConditionalOnProperty 注解。我们把NotificationSender bean配置成,只有在定义了属性 notification.service 时才会被加载:

1
2
3
4
5
@Bean(name = "emailNotification")
@ConditionalOnProperty(prefix = "notification", name = "service")
public NotificationSender notificationSender() {
return new EmailNotification();
}

如我们所见,prefix 和 name 属性用于表示应该检查的配置属性。

最后,我们需要添加拼图的最后一个缺失部分。 让我们在 application.properties 文件中定义我们的自定义属性:

1
notification.service=email

4. 高级配置

@ConditionalOnProperty 可以注解帮助我们根据是否存在配置有条件地注册 bean。

然而,我们可以用这个注解做更多的事情。 那么,让我们一探究竟吧!

假设我们想要添加另一个通知服务——例如,一个帮助我们发送 SMS 通知的服务。

为此,我们需要创建另一个 NotificationSender 实现:

1
2
3
4
5
6
public class SmsNotification implements NotificationSender {
@Override
public String send(String message) {
return "SMS Notification: " + message;
}
}

由于我们有两个实现,让我们看看如何使用 @ConditionalOnProperty 有条件地加载正确的 NotificationSender bean。

为此,注解提供了 havingValue 属性。 有趣的是,它修饰的属性必须具有值的,以便把将对应的 bean 添加到 Spring 容器中。

现在,让我们指定在什么条件下在上下文中注册 SmsNotification 实现:

1
2
3
4
5
@Bean(name = "smsNotification")
@ConditionalOnProperty(prefix = "notification", name = "service", havingValue = "sms")
public NotificationSender notificationSender2() {
return new SmsNotification();
}

通过 havingValue 属性,我们明确了仅在 notification.service 设置为 sms 时才加载 SmsNotification。

值得一提的是 @ConditionalOnProperty 还有一个属性叫做 matchIfMissing。 此属性指定在属性不可用的情况下是否应匹配条件。

现在,让我们将所有部分放在一起并编写一个简单的测试用例来确认一切都按预期进行:

1
2
3
4
5
6
7
8
9
10
11
@Test
public void whenValueSetToEmail_thenCreateEmailNotification() {
this.contextRunner.withPropertyValues("notification.service=email")
.withUserConfiguration(NotificationConfig.class)
.run(context -> {
assertThat(context).hasBean("emailNotification");
NotificationSender notificationSender = context.getBean(EmailNotification.class);
assertThat(notificationSender.send("Hello From Baeldung!")).isEqualTo("Email Notification: Hello From Baeldung!");
assertThat(context).doesNotHaveBean("smsNotification");
});
}

5. 结论

在这个简短的教程中,我们强调了使用 @ConditionalOnProperty 注解的目的。 然后,我们通过一个实际的例子展示了如何使用它来有条件地加载Spring bean。

与往常一样,本教程的完整源代码可在 GitHub 上获得。