尽管未来似乎很遥远,但它实际上现在就开始了。
上篇文章《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自定义条件注解就搞定了。