这篇文章介绍Spring 4的@Conditional注解。
SPEL有一个三元运算符(if-then-else)可以在配置文件中当作条件语句,如下:
<bean id="flag"> <constructor-arg value="#{systemProperties['system.propery.flag'] ?: false }" /> </bean> <bean id="testBean"> <property name="prop" value="#{ flag ? 'yes' : 'no' }"/> </bean>
testBean的prop动态依赖于flag的值。
<!-- 如果没有设置profile,default.xml将被加载 --> <!-- 必须放置在配置文件的最底下,后面再也没有bean的定义 --> <beans profile="default"> <import resource="classpath:default.xml" /> </beans> <!-- some other profile --> <beans profile="otherProfile"> <import resource="classpath:other-profile.xml" /> </beans>
官方文档定义:“Indicates that a component is only eligible for registration when all specified conditions match”,意思是 只有满足一些列条件之后创建一个bean 。
除了自己自定义Condition之外,Spring还提供了很多Condition给我们用
@ConditionalOnClass : classpath中存在该类时起效 @ConditionalOnMissingClass : classpath中不存在该类时起效 @ConditionalOnBean : DI容器中存在该类型Bean时起效 @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效 @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效 @ConditionalOnExpression : SpEL表达式结果为true时 @ConditionalOnProperty : 参数设置或者值一致时起效 @ConditionalOnResource : 指定的文件存在时起效 @ConditionalOnJndi : 指定的JNDI存在时起效 @ConditionalOnJava : 指定的Java版本存在时起效 @ConditionalOnWebApplication : Web应用环境下起效 @ConditionalOnNotWebApplication : 非Web应用环境下起效
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE, ElementType.METHOD) public @interface Conditional{ Class<? extends Condition>[] value(); }
类级别可以放在注标识有@Component(包含@Configuration)的类上
作为一个meta-annotation,组成自定义注解
如果一个@Configuration的类标记了@Conditional,所有标识了@Bean的方法和@Import注解导入的相关类将遵从这些条件。
condition接口定义如下:
public interface Condition { /** * Determine if the condition matches. * @param context the condition context * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class} * or {@link org.springframework.core.type.MethodMetadata method} being checked. * @return {@code true} if the condition matches and the component can be registered * or {@code false} to veto registration. */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
示例1: 下面看一个例子:
package com.dxz.demo.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class LinuxCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment().getProperty("os.name").contains("Linux"); } } package com.dxz.demo.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class WindowsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment().getProperty("os.name").contains("Windows"); } }
我们有两个类LinuxCondition 和WindowsCondition 。两个类都实现了Condtin接口,重载的方法返回一个基于操作系统类型的布尔值。
下面我们定义两个bean,一个符合条件另外一个不符合条件:
package com.dxz.demo.condition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class MyConfiguration { @Bean(name = "emailerService") @Conditional(WindowsCondition.class) public EmailService windowsEmailerService() { return new WindowsEmailService(); } @Bean(name = "emailerService") @Conditional(LinuxCondition.class) public EmailService linuxEmailerService() { return new LinuxEmailService(); } }
当符合某一个条件的时候,这里的@Bean才会被初始化。
测试相关其它类:
package com.dxz.demo.condition; public interface EmailService { public void sendEmail(); } package com.dxz.demo.condition; public class WindowsEmailService implements EmailService { public void sendEmail() { System.out.println("send windows email"); } } package com.dxz.demo.condition; public class LinuxEmailService implements EmailService { public void sendEmail() { System.out.println("send linux email"); } } package com.dxz.demo.condition; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service public class ConditionClient { @Autowired private EmailService emailService; @Scheduled(initialDelay = 3000, fixedDelay = 10000) public void test() { emailService.sendEmail(); } }
结果:
示例2:@ConditionalOnProperty来控制Configuration是否生效
Spring Boot通过@ConditionalOnProperty来控制Configuration是否生效
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { String[] value() default {}; //数组,获取对应property名称的值,与name不可同时使用 String prefix() default "";//property名称的前缀,可有可无 String[] name() default {};//数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用 String havingValue() default "";//可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置 boolean matchIfMissing() default false;//缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错 boolean relaxedNames() default true;//是否可以松散匹配,至今不知道怎么使用的 } }
通过其两个属性name以及havingValue来实现的,其中name用来从application.properties中读取某个属性值。 如果该值为空,则返回false; 如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。 如果返回值为false,则该configuration不生效;为true则生效。
@Configuration //在application.properties配置"mf.assert",对应的值为true @ConditionalOnProperty(prefix="mf",name = "assert", havingValue = "true") public class AssertConfig { @Autowired private HelloServiceProperties helloServiceProperties; @Bean public HelloService helloService(){ HelloService helloService = new HelloService(); helloService.setMsg(helloServiceProperties.getMsg()); return helloService; } } s
原文链接:https://www.cnblogs.com/duanxz/p/7489046.html