尽管未来似乎很遥远,但它实际上现在就开始了。
上篇文章《SpringBoot 条件注解一览无余》介绍了Springboot有哪些条件注解及一些属性的含义,本篇文章将介绍一下如何自定义条件注解。
话不多说,上代码。
在自定义条件之前,咱先看看Springboot条件注解是怎么实现的,就挑 @ConditionalOnProperty 注解看一下。下图是@ConditionalOnProperty 注解的实现,从实现中可以看到除了元注解之外,它是被 @Conditional(OnPropertyCondition.class) 标记的,被@Conditional() 注解标记的注解表示该注解是个条件注解,@Conditional() 注解的value就对应着该注解的具体实现逻辑类。下面我们就主要看看 OnPropertyCondition 类是怎么实现的。
接下来我们将进行实操。

OnPropertyCondition 类继承了 SpringBootCondition 类,并重写了 getMatchOutcome() 方法,而 SpringBootCondition 类则是实现了 Condition接口并进行了封装,加入了类似日志之类的东西,感兴趣的可以细细看下,至此OnPropertyCondition 类就分析完了。
从上面的分析中我们可以发现,自定义条件注解主要分为两步:
@Conditional() 注解标记。SpringBootCondition 类并重写 getMatchOutcome() 方法,二是实现 Condition接口并重写 matches() 方法。SpringBootCondition 是 Condition接口的实现并进行了封装,推荐使用SpringBootCondition ,当然,如果自定义条件注解的实现类已经有父类,使用 Condition接口也是没问题的。其实还有一种选择那就是实现 ConfigurationCondition ,它继承了Condition接口,并在其基础上增加了一些针对配置类的条件判断方法,使用它也可以实现自定义条件注解,下篇文章将介绍一下 ConfigurationCondition接口及其和Condition接口的区别。

OnPropertyCondition 类

SpringBootCondition

OnPropertyCondition类图
众所周知,某练习两年半的练习生技能包里有唱跳、Rap、打篮球三项技能,下面就以该练习生技能包为案例,激活什么技能就用什么技能。

侵删
新建一个技能条件注解 ConditionalOnSkill ,里面就一个value属性
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSkillCondition.class)
public @interface ConditionalOnSkill {
/**
* 技能
* @return
*/
String value();
}
新建一个 技能条件注解实现类OnSkillCondition ,该类中定义一个 PROPERTY_NAME 常量,该常量的值最终会从配置文件中读取。
public class OnSkillCondition extends SpringBootCondition {
private static final String PROPERTY_NAME = "brother-rooster.skill";
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取所有被自定义条件注解标记的填写的属性值
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSkill.class.getName());
//获取常量值
String value = attributes.get("value").toString();
Environment environment = context.getEnvironment();
//读取PROPERTY_NAME值
String property = environment.getProperty(PROPERTY_NAME);
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnSkill.class);
ConditionMessage conditionMessage = message.foundExactly(value);
boolean match = value.equals(property);
return new ConditionOutcome(match, conditionMessage);
}
}
创建一个BrotherRoosterSkill接口,用于测试条件注解,然后分别创建3个实现类:篮球技能BrotherRoosterSkillBasketball、rap技能 BrotherRoosterRap、唱跳技能 BrotherRoosterSkillSing
public interface BrotherRoosterSkill {
void printSkill();
}
public class BrotherRoosterSkillBasketball implements BrotherRoosterSkill {
@Override
public void printSkill() {
System.out.println("打篮球");
}
}
public class BrotherRoosterRap implements BrotherRoosterSkill {
@Override
public void printSkill() {
System.out.println("rap");
}
}
public class BrotherRoosterSkillSing implements BrotherRoosterSkill{
@Override
public void printSkill() {
System.out.println("唱跳");
}
}
ConditionConfig配置类 注入技能Bean@Component
public class ConditionConfig {
@Bean("brotherRoosterSkill")
@ConditionalOnSkill("basketball")
BrotherRoosterSkill brotherRoosterSkillBasketball(){
System.out.println("打篮球技能激活。。。。。");
return new BrotherRoosterSkillBasketball();
}
@Bean("brotherRoosterSkill")
@ConditionalOnSkill("rap")
BrotherRoosterSkill brotherRoosterSkillRap(){
System.out.println("Rap技能激活。。。。。");
return new BrotherRoosterRap();
}
@Bean("brotherRoosterSkill")
@ConditionalOnSkill("sing")
BrotherRoosterSkill brotherRoosterSkillSing(){
System.out.println("唱跳技能激活。。。。。");
return new BrotherRoosterSkillSing();
}
}
在配置文件中加入如下配置:
#kk技能包
brother-rooster.skill=sing
写一个单元测试类:
@SpringBootTest
public class ConditionalTest {
@Resource
private BrotherRoosterSkill brotherRoosterSkill;
@Test
public void testBrotherRoosterSkill(){
System.out.println("鸡哥激活了哪个技能包:");
brotherRoosterSkill.printSkill();
}
}
启动测试,控制台打印结果如下图所示,可以看到 BrotherRoosterSkillSing被实例化了。

把配置改成 brother-rooster.skill=basketball , 控制台打印结果如下图所示,可以看到 BrotherRoosterSkillBasketball被实例化了。

至此,一个简单的Springboot自定义条件注解就搞定了。