Spring Conditional Annotations – Spring的条件性注解

最后修改: 2021年 5月 26日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll take a look at the @Conditional annotation. It’s used to indicate whether a given component is eligible for registration based on a defined condition.

在本教程中,我们将看看@Conditional注解。它被用来表明一个给定的组件是否有资格根据定义的条件进行注册。

We’ll learn how to use predefined conditional annotations, combine them with different conditions as well as create our custom, condition-based annotations.

我们将学习如何使用预定义的条件注释,将它们与不同的条件相结合,以及创建我们自定义的、基于条件的注释。

2. Declaring Conditions

2.宣布条件

Before we move into the implementation, let’s firstly see in which situations we could make use of conditional annotations.

在我们进入实现之前,首先让我们看看在哪些情况下我们可以使用条件注释。

The most common usage would be to include or exclude the whole configuration class:

最常见的用法是包括或排除整个配置类

@Configuration
@Conditional(IsDevEnvCondition.class)
class DevEnvLoggingConfiguration {
    // ...
}

Or just a single bean:

或者只是一颗Bean。

@Configuration
class DevEnvLoggingConfiguration {
    
    @Bean
    @Conditional(IsDevEnvCondition.class)
    LoggingService loggingService() {
        return new LoggingService();
    }
}

By doing so, we can base the behavior of our application on given conditions. For instance, the type of environment or specific needs of our clients. In the above example, we initialize additional logging services only for the development environment.

通过这样做,我们可以将我们的应用程序的行为建立在给定的条件之上。例如,环境的类型或我们客户的特定需求。在上面的例子中,我们只为开发环境初始化了额外的日志服务。

Another way of making the component conditional would be to place the condition directly on the component class:

另一种使组件具备条件的方法是将条件直接放在组件类上。

@Service
@Conditional(IsDevEnvCondition.class)
class LoggingService {
    // ...
}

We can apply the above example to any bean declared with the @Component@Service@Repository, or @Controller annotations.

我们可以将上述例子应用于任何用@Component@Service@Repository@Controller注解声明的bean。

3. Predefined Conditional Annotations

3.预定义的条件注释

Spring comes with a set of predefined conditional annotations. Let’s go through some of the most popular ones.

Spring有一套预定义的条件注释。让我们来看看一些最流行的注释。

Firstly, let’s see how we can base a component on a configuration property value:

首先,让我们看看我们如何将一个组件建立在一个配置属性值上

@Service
@ConditionalOnProperty(
  value="logging.enabled", 
  havingValue = "true", 
  matchIfMissing = true)
class LoggingService {
    // ...
}

The first attribute, value, tells us what configuration property we’ll be looking at. The second one, havingValue, defines a value that’s required for this condition. And lastly, the matchIfMissing attribute tells the Spring whether the condition should be matched if the parameter is missing.

第一个属性,value,告诉我们要看的是什么配置属性。第二个属性,havingValue,定义了这个条件所需的值。最后,matchIfMissing属性告诉Spring,如果参数丢失,条件是否应该被匹配。

Similarly, we can base the condition on an expression:

同样地,我们可以将条件建立在一个表达式上

@Service
@ConditionalOnExpression(
  "${logging.enabled:true} and '${logging.level}'.equals('DEBUG')"
)
class LoggingService {
    // ...
}

Now, Spring will create the LoggingService only when both the logging.enabled configuration property is set to true, and the logging.level is set to DEBUG.

现在,只有当logging.enabled配置属性被设置为true,logging.level被设置为DEBUG,Spring才会创建LoggingService

Another condition we can apply is to check whether a given bean was created:

我们可以应用的另一个条件是检查一个给定的bean是否被创建。

@Service
@ConditionalOnBean(CustomLoggingConfiguration.class)
class LoggingService {
    // ...
}

Or a given class exists on the classpath:

或者一个给定的类存在于classpath上。

@Service
@ConditionalOnClass(CustomLogger.class)
class LoggingService {
    // ...
}

We can achieve the opposite behavior by applying the @ConditionalOnMissingBean or @ConditionalOnMissingClass annotations.

我们可以通过应用@ConditionalOnMissingBean@ConditionalOnMissingClass注解实现相反的行为。

In addition, we can depend our components on a given Java version:

此外,我们可以将我们的组件依赖给定的Java版本

@Service
@ConditionalOnJava(JavaVersion.EIGHT)
class LoggingService {
    // ...
}

In the above example, the LoggingService will be created only when the runtime environment is Java 8.

在上面的例子中,LoggingService只有在运行环境为Java 8时才会被创建。

Finally, we can use the @ConditionalOnWarDeployment annotation to enable bean only in war packaging:

最后,我们可以使用@ConditionalOnWarDeployment注解来启用仅在战争包装中的bean。

@Configuration
@ConditionalOnWarDeployment
class AdditionalWebConfiguration {
    // ...
}

Note, that for applications with embedded servers, this condition will return false.

注意,对于有嵌入式服务器的应用,这个条件将返回错误。

4. Defining Custom Conditions

4.定义自定义条件

Spring allows us to customize the behavior of the @Conditional annotation by creating our custom condition templates. To create one, we simply need to implement the Condition interface:

Spring允许我们通过创建我们的自定义条件模板来定制@Conditional注释的行为。要创建一个模板,我们只需要实现Condition 接口。

class Java8Condition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return JavaVersion.getJavaVersion().equals(JavaVersion.EIGHT);
    }
}

The matches method tells Spring whether the condition has passed or not. It has two arguments that give us respectively information about the context where the bean will initialize and the metadata of the used @Conditional annotation.

matches方法告诉Spring条件是否通过。它有两个参数,分别给了我们关于Bean将被初始化的上下文和所使用的@Conditional注释的元数据的信息。

As we can see, in our example, we just check if the Java version is 8.

正如我们所看到的,在我们的例子中,我们只是检查Java版本是否为8。

After that, we should place our new condition as an attribute in the @Conditional annotation:

之后,我们应该把我们的新条件作为一个属性放在@Conditional注释中。

@Service
@Conditional(Java8Condition.class)
public class Java8DependedService {
    // ...
}

This way, the Java8DependentService will be created only where the condition from Java8Condition class is matched.

这样,Java8DependentService将只在Java8Condition类的条件被匹配的地方被创建。

5. Combining Conditions

5.组合条件

For more complex solutions, we can group conditional annotations with OR or AND logical operators.

对于更复杂的解决方案,我们可以用OR或AND逻辑运算符分组条件注释

To apply the OR operator, we need to create a custom condition extending the AnyNestedCondition class. Inside it, we need to create an empty static class for each condition and annotate it with a proper @Conditional implementation.

为了应用OR运算符,我们需要创建一个自定义条件,扩展AnyNestedCondition类。在它里面,我们需要为每个条件创建一个空的static类,并用适当的@Conditional实现来注释它。

For example, let’s create a condition that requires either Java 8 or Java 9:

例如,让我们创建一个条件,需要Java 8或Java 9。

class Java8OrJava9 extends AnyNestedCondition {
    
    Java8OrJava9() {
        super(ConfigurationPhase.REGISTER_BEAN);
    }
    
    @Conditional(Java8Condition.class)
    static class Java8 { }
    
    @Conditional(Java9Condition.class)
    static class Java9 { }
    
}

The AND operator, on the other hand, is much simpler. We can simply group the conditions:

另一方面,AND运算符要简单得多。我们可以简单地将这些条件分组。

@Service
@Conditional({IsWindowsCondition.class, Java8Condition.class})
@ConditionalOnJava(JavaVersion.EIGHT)
public class LoggingService {
    // ...
}

In the above example, the LoggingService will be created only when both the IsWindowsCondition and Java8Condition are matched.

在上面的例子中,只有当IsWindowsConditionJava8Condition都被匹配时,LoggingService才会被创建。

6. Summary

6.归纳总结

In this article, we’ve learned how to use and create conditional annotations. As always, all the source code is available over on GitHub.

在这篇文章中,我们已经学会了如何使用和创建条件注释。一如既往,所有的源代码都可以在GitHub上找到