在春季使用条件式

在本文中,我想描述一个非常有用且经常使用的条件注释和条件接口。


Spring上下文是各种bean的巨大容器,包括spring本身和定制bean。 您始终想为此仓库动物园提供灵活的管理工具。 为此仅创建了@Conditional批注


管理Spring上下文的最常见方法是通过配置文件。 它们使您可以快速轻松地控制Bean的创建。 但是有时可能需要进行更精细的调整。


例如,在测试过程中会出现一个问题:在开发人员的机器上进行单元测试需要X型容器才能完成工作,在构建服务器上运行相同的测试时,需要Y-bin,而在生产环境中则需要Z-bin。决定。 就像在不同步的团队中工作时一样,有人没有时间在截止日期之前完成其修订,因此您的功能已准备就绪。 有必要适应这些条件并改变行为。 也就是说,增加了无需重新编译即可更改应用程序上下文的功能,例如,仅更改配置中的一个参数。


请更详细地考虑此注释。 在源代码的每个bin上方,我们可以添加@Conditional ,spring在创建此批注时将自动检查此批注中指定的条件。


在官方文档中,声明如下:


@Target(value={TYPE,METHOD}) @Retention(value=RUNTIME) @Documented public @interface Conditional 

同时,您需要向其中转移一组条件:


 Class<? extends Condition>[] 

条件是其中包含方法的功能接口


 boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) 

让我们通过一个生动的例子来检查它在实践中是如何工作的。 我们的应用程序具有以soap / rest-service形式和JMS​​形式的接口。 但是管理员没有时间及时准备适当的基础结构-我们无法使用JMS。
在我们的项目中,JMS有一些Java配置:


 @Configuration public class JmsConfig { ... } 

Spring找到此配置并开始对其进行初始化。 接下来,拉出所有其他从属bean,例如,从队列中读取。 要禁用此配置的创建,我们将使用条件导数注释-ConditioanalOnProperty


 @ConditionalOnProperty( value="project.mq.enabled", matchIfMissing = false) @Configuration public class JmsConfig { ... } 

在这里,我们告诉上下文构建器,只有在设置文件中存在project.mq.enabled常量的正值时,我们才创建此配置。
现在,让我们继续从属bean并用ConditioanalOnBean批注对其进行标记,这将防止弹簧创建依赖于我们的配置的bean。


 @ConditionalOnBean(JmsConfig.class) @Component public class JmsConsumer { ... } 

因此,使用单个参数,我们可以禁用不需要的应用程序组件,然后通过更改配置将它们添加到上下文中。


与该框架一起,存在大量现成的注释,它们满足了开发人员99%的需求(将在本文稍后进行介绍)。 但是,如果您需要处理某些特定情况该怎么办。 为此,您可以在Spring中添加自己的自定义逻辑。


假设我们有一些bean- SuperDBLogger ,只有在我们的任何容器上都有@Loggable批注时,才要创建。 它在代码中的外观:


 @Component @ConditionalOnLoggableAnnotation public class SuperDBLogger 

考虑@ConditionalOnLoggableAnnotation批注的工作方式


 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnLoggableAnnotation.class) public @interface ConditionalOnLoggableAnnotation { } 

我们不需要任何其他参数,现在让我们继续进行逻辑本身-OnLoggableAnnotation类的内容。 在其中,我们重新定义了matchs方法,在该方法中,我们实现了对程序包中标记的bean的搜索。


 public class OnLoggableAnnotation implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { ClassPathScanner scanner = new ClassPathScanner(); scanner.addIncludeFilter(new AnnotationTypeFilter(Loggable.class)); Set<BeanDefinition> bd = scanner.findInPackage("ru.habr.mybeans"); if (!bd.isEmpty()) return true; return false; } } 

因此,我们根据Spring现在创建的规则创建了SuperDBLogger 。 对于SpringBoot的奉献者,框架的创建者创建了SpringBootCondition ,它是Condition的后继者。 它与重新定义的方法的签名不同:


 public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata); 

也就是说,除了答案,我们是否需要一个垃圾箱,您可以添加一条消息,然后可以在spring日志中看到该消息,了解为什么创建垃圾箱或不创建垃圾箱。


为了创建更复杂的条件,可以组合各种条件;这些机制提供了AnyNestedCondition,AllNestedConditions和NoneNestedConditions类 。 假设我们要创建两个条件,以便在执行其中一个条件时创建我们的bean。 为此,请创建您自己的类并从AnyNestedCondition继承它。


 public class AnnotationAndPropertyCondition extends AnyNestedCondition { public AnnotationAndPropertyCondition() { super(REGISTER_BEAN); } @ConditionalOnProperty(value = "db.superLogger") static class Condition1 {} @ConditionalOnLoggableAnnotation static class Condition2 {} } 

该类无需额外标记任何注释; spring本身将找到它并正确处理它。 用户只需要指出在配置的哪个阶段满足条件: ConfigurationPhase .REGISTER_BEAN-创建常规bean时, ConfigurationPhase .PARSE_CONFIGURATION-使用配置时(即,标有@Configuration批注的bin)。


同样,对于类AllNestedConditionsNoneNestedConditions ,第一个监视所有条件,第二个确保不满足任何条件。


另外,为了检查几个条件,您可以将几个带有条件的类传递给@Conditional 。 例如, @Conditional({OnLoggableAnnotation.class,AnnotationAndPropertyCondition.class}) 。 两者都必须返回true,以便满足条件并创建Bean。


正如我上面提到的,弹簧已经有许多现成的解决方案,如下表所示。


注解内容描述
条件OnBean如果BeanFactory中存在所需的bean,则满足条件。
条件类如果所需的类在类路径中,则满足条件。
有条件的云平台当特定平台处于活动状态时,将满足条件。
条件表达式当SpEL表达式返回正值时,条件为true。
Java的条件使用特定版本的JVM启动应用程序时,将满足条件。
有条件的仅当可以通过JNDI获得特定资源时,才满足条件。
条件豆如果BeanFactory中缺少所需的bean,则满足条件。
缺席条件类如果所需的类不在类路径中,则条件为true。
ConditionalOnNotWebApplication如果应用程序上下文不是Web上下文,则条件为true。
条件财产如果在设置文件中指定了必要的参数,则满足该条件。
条件资源如果所需的资源存在于类路径中,则满足条件。
有条件的单一候选人如果指定类的bean已经包含在BeanFactory中,并且它是唯一的,则满足该条件。
ConditionalOnWebApplication如果应用程序上下文是Web上下文,则条件为true。

所有这些都可以一起应用于一个bean的定义。


因此, @ Conditional是一个功能非常强大的上下文配置工具,使应用程序更加灵活。 但是值得考虑的一个事实是,您需要仔细使用此批注,因为上下文的行为变得不像使用配置文件时那样明显-使用大量已配置的bin,您很快就会感到困惑。 建议在您的项目中仔细记录并记录其应用程序,否则代码支持会造成困难。

Source: https://habr.com/ru/post/zh-CN462541/


All Articles