springBoot @Condition注解的使用原理

493 阅读2分钟

1:在上一篇文章中,我们大致能知道自定义的starter的配置类是如何被SpringBoor找到并且解析的,但是SpringBoot还会对这些配置类进行过滤,比如:当你配置了某些属性的时候这个配置类才会生效,当某个Bean存在这个配置类才生效,那么底层原理是怎么样的呢??

2:定义一个注解

//这个注解是自定义的
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
//重点在这里,我们使用了@Condition注解,然后里面还有一个类MyCondition,
//这个类也是要我们自定义的
@Conditional(MyCondition.class)
public @interface MyConditionOnProperty {
    //我们自己定义的一个注解属性
    String value() default "";
}

3:实现MyCondition.class

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.StringUtils;

public class MyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取我们自定义注解的value属性,这里要写我们注解的全路径名
        String value = (String) metadata.getAnnotationAttributes("com.leader.test.MyConditionOnProperty")
                .get("value");
        //判断是否存在这个配置,如果不存在就返回false,存在就返回true
        if(StringUtils.isEmpty(context.getEnvironment().getProperty(value))) {
            return false;
        }
        return true;
    }
}

4:动手测试:创建一个A类,

  // 这个类很简单,这个类在SpringBoot启动之后就会被注册到Spring容器中,因为有@Component注解存在
  @Component
  //这里使用我们自定义的注解,表示只有存在配置 coco 的时候,这个类才有效
  @MyConditionOnProperty(value = "coco")
  public class A {
  }
  

5:现在我们来测试下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootTestCo implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    // 首先 你可以不在application.properties文件中配置 coco=123这样的配置
    // 你会发现A类为空,
    // 然后你再配置文件中配置 coco = xxx 这样的配置,再测试一下,就会发现A类有了
    @Test
    public void test() {
        A contextBean = applicationContext.getBean(A.class);
        System.out.println(contextBean);
    }
}

6:通过前面的例子你也许会发现@Condition前缀的注解的大概原理了,我们挑几个@ConditionalOnProperty,@ConditionalOnBean。我们看下这个注解的构造

  @Retention(RetentionPolicy.RUNTIME)
  @Target({ ElementType.TYPE, ElementType.METHOD })
  @Documented
  //@ConditionalOnProperty注解的实现逻辑就在OnPropertyCondition.class里面
  @Conditional(OnPropertyCondition.class)
  public @interface ConditionalOnProperty {}
  
  @Target({ ElementType.TYPE, ElementType.METHOD })
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  //@ConditionalOnBean注解的实现逻辑就在OnPropertyCondition.class里面
  @Conditional(OnBeanCondition.class)
  public @interface ConditionalOnBean {}

  是的,注解只有一个作用,那就是标志,而真正的处理逻辑就是在@Condition注解里面的实现类
  

7:总结

  SpringBoot中@ConditionalOnProperty@ConditionalOnBean等等之类的注解,都会有一个相关逻辑处理的类,
  而这个类就是这个注解上@Condition注解中包含的那个类,这个类呢会实现Condition接口,即使这个类没有实现,那么它的父类也会实现,
  实现这个类就会实现 
  public final boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata) {}
  这个方法,比如@ConditionalOnProperty注解,机会判断SpringBoot中是否存在某个配置,如果存在就会返回true,
  如果不存在就返回false,而我们上一篇文章是否会被解析也就是根据这个方法来进行判断的,返回true那就解析,
  返回false,那就不解析