@Conditional是SpringFramework的功能,SpringBoot在其基础上进行了一定的扩展。
然而实际开发过程中,Spring或SpringBoot提供的注解并不能满足我们的需求,此时则需要我们自己去定义相关注解。
查看Conditional注解的源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
可以看到Conditional中需要传入Condition类型的Class
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata the 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 the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
所以我们可以创建一个实现了Condition接口的类,重写matches方法即可。
ConditionContext是Condition的上下文,可以获取BeanDefinitionRegistry
、ConfigurableListableBeanFactory
、Environment
等数据。
AnnotatedTypeMetadata可以用于获取枚举的信息。
例如我现在有一个需要基于操作系统进行装配选择的场景。
可以通过@ConditionalOnExpression
注解,通过SpEL表达式进行判断,如
@ConditionalOnExpression("T(org.apache.commons.lang3.SystemUtils).IS_OS_LINUX")
@ConditionalOnExpression("T(org.apache.commons.lang3.SystemUtils).IS_OS_WINDOWS")
也可以使用自定义注解去实现。
创建Condition接口的实现类
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
@Slf4j
public class OnOsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String conditionOsName = (String) metadata.getAnnotationAttributes(ConditionalOnOS.class.getName()).get("value");
if (log.isDebugEnabled()){
log.debug("OS_NAME IS {}",SystemUtils.OS_NAME);
}
return StringUtils.startsWithIgnoreCase(SystemUtils.OS_NAME,conditionOsName);
}
}
创建自定义注解ConditionOnOs
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnOsCondition.class)
public @interface ConditionalOnOS {
String value();
}
此时,我们如果需要对类进行选择装配,则可以在相关类上添加注解,如
@ConditionalOnOS("Linux")
@ConditionalOnOS("Mac")
@ConditionalOnOS("Windows")
也可以对此注解进行二次封装,此时需要对ConditionalOnOS
添加继承注解@Inherited
,同时使注解的作用域可作用域注解之上
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(value = {ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnOsCondition.class)
@Inherited
public @interface ConditionalOnOS {
String value();
}
创建基于操作系统的的注解
@Documented
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ConditionalOnOS("Windows")
public @interface ConditionalOnWindows {
}
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@ConditionalOnOS("Linux")
public @interface ConditionOnLinux {
}
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@ConditionalOnOS("Mac")
public @interface ConditionOnMac {
}
这样就可以直接使用二次封装过的接口进行Condition配置了