文章目录
- 实践出真知
- 先分析Springboot提供的注解
- 实操
上篇文章介绍了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接口的区别。



实操
众所周知,某练习两年半的练习生技能包里有唱跳、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);
}
}- 创建Bean接口及实现类
创建一个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("唱跳");
}
}- 新建配置注入技能包的Bean
创建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自定义条件注解就搞定了。
















