• SpringBoot @ConditionalOnBean、@ConditionalOnMissingBean注解源码分析与示例


    前言:

    Spring4推出了@Conditional注解,方便程序根据当前环境或者容器情况来动态注入bean,对@Conditional注解不熟悉的朋友可移步至 Spring @Conditional注解 详细讲解及示例 这篇博客进行学习。

    继@Conditional注解后,又基于此注解推出了很多派生注解,比如@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnExpression、@ConditionalOnClass......动态注入bean变得更方便了。本篇将讲解@ConditionalOnBean注解。

    配置类中有两个Computer类的bean,一个是笔记本电脑,一个是备用电脑。如果当前容器中已经有电脑bean了,就不注入备用电脑,如果没有,则注入备用电脑,这里需要使用到@ConditionalOnMissingBean。

    1. @Configuration
    2. public class BeanConfig {
    3. @Bean(name = "notebookPC")
    4. public Computer computer1(){
    5. return new Computer("笔记本电脑");
    6. }
    7. @ConditionalOnMissingBean(Computer.class)
    8. @Bean("reservePC")
    9. public Computer computer2(){
    10. return new Computer("备用电脑");
    11. }
    12. }

    这个注解就实现了功能,这个@ConditionalOnMissingBean为我们做了什么呢?我们来一探究竟.。

    一探究竟:

    首先,来看@ConditionalOnMissingBean的声明:

    1. //可以标注在类和方法上
    2. @Target({ElementType.TYPE, ElementType.METHOD})
    3. @Retention(RetentionPolicy.RUNTIME)
    4. @Documented
    5. //使用了@Conditional注解,条件类是OnBeanCondition
    6. @Conditional({OnBeanCondition.class})
    7. public @interface ConditionalOnMissingBean {
    8. Class<?>[] value() default {};
    9. String[] type() default {};
    10. Class<?>[] ignored() default {};
    11. String[] ignoredType() default {};
    12. Class<? extends Annotation>[] annotation() default {};
    13. String[] name() default {};
    14. SearchStrategy search() default SearchStrategy.ALL;
    15. }

    这时候,我们就看到了我们熟悉的@Conditional注解,OnBeanCondition作为条件类。

    OnBeanCondition类的声明:

    1. //定义带注释的组件的排序顺序,2147483647即为默认值
    2. @Order(2147483647)
    3. class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {

    它继承了SpringBootCondition类,OnBeanCondition类中没有matches方法,而SpringBootCondition类中有实现matches方法。OnBeanCondition还实现了ConfigurationCondition,ConfigurationCondition接口不熟悉的读者可以到Spring ConfigurationCondition接口详解 了解接口。OnBeanCondition类重写了getConfigurationPhase()方法,表示在注册bean的时候注解生效:

    1. public ConfigurationPhase getConfigurationPhase() {
    2. return ConfigurationPhase.REGISTER_BEAN;
    3. }

    就从matches方法开始:

    1. //SpringBootCondition类中的matches方法
    2. public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    3. //获取当前的类名或者方法名(由标注的位置决定)
    4. String classOrMethodName = getClassOrMethodName(metadata);
    5. try {
    6. //关键代码:这里就会判断出结果
    7. ConditionOutcome outcome = this.getMatchOutcome(context, metadata);
    8. //存入日志
    9. this.logOutcome(classOrMethodName, outcome);
    10. //存入记录
    11. this.recordEvaluation(context, classOrMethodName, outcome);
    12. //最后返回ConditionOutcome的isMatch就是返回boolean类型结果
    13. return outcome.isMatch();
    14. } catch (NoClassDefFoundError var5) {
    15. throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);
    16. } catch (RuntimeException var6) {
    17. throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
    18. }
    19. }

    关键代码在OnBeanCondition的getMatchOutcome方法上:

    1. /**
    2. * 获得判断结果的方法,ConditionOutcome类中存着boolean类型的结果
    3. */
    4. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    5. //返回一个新的ConditionMessage
    6. ConditionMessage matchMessage = ConditionMessage.empty();
    7. OnBeanCondition.BeanSearchSpec spec;
    8. List matching;
    9. //这是metadata会调用isAnnotated方法判断当前标注的注解是不是ConditionalOnMissingBean
    10. //其实@ConditionalOnBean、@ConditionalOnMissingBean和@ConditionalOnSingleCandidate都是使用这个条件类,所以这里做判断
    11. if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
    12. spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);
    13. matching = this.getMatchingBeans(context, spec);
    14. if (matching.isEmpty()) {
    15. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnBean.class, new Object[]{spec}).didNotFind("any beans").atAll());
    16. }
    17. matchMessage = matchMessage.andCondition(ConditionalOnBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching);
    18. }
    19. if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
    20. OnBeanCondition.BeanSearchSpec spec = new OnBeanCondition.SingleCandidateBeanSearchSpec(context, metadata, ConditionalOnSingleCandidate.class);
    21. matching = this.getMatchingBeans(context, spec);
    22. if (matching.isEmpty()) {
    23. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).didNotFind("any beans").atAll());
    24. }
    25. if (!this.hasSingleAutowireCandidate(context.getBeanFactory(), matching, spec.getStrategy() == SearchStrategy.ALL)) {
    26. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).didNotFind("a primary bean from beans").items(Style.QUOTE, matching));
    27. }
    28. matchMessage = matchMessage.andCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).found("a primary bean from beans").items(Style.QUOTE, matching);
    29. }
    30. //如果当前注入的bean是@ConditionalOnMissingBean
    31. if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
    32. //返回一个spec(说明),这里的spec规定了搜索的内容,比如搜索策略、需要搜索的类名......
    33. spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
    34. //主要的搜索实现在这个方法里,最后返回一个list
    35. matching = this.getMatchingBeans(context, spec);
    36. //判断搜索出来的结果
    37. if (!matching.isEmpty()) {
    38. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching));
    39. }
    40. matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, new Object[]{spec}).didNotFind("any beans").atAll();
    41. }
    42. return ConditionOutcome.match(matchMessage);
    43. }

    spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);

    这句中,相当于从内部类中将标注@ConditionalOnMissingBean注解时的属性都取出来:

    1. BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata, Class<?> annotationType) {
    2. this.annotationType = annotationType;
    3. MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
    4. //将attributes这个map中的数据放到对应的list成员变量中
    5. this.collect(attributes, "name", this.names);
    6. this.collect(attributes, "value", this.types);
    7. this.collect(attributes, "type", this.types);
    8. this.collect(attributes, "annotation", this.annotations);
    9. this.collect(attributes, "ignored", this.ignoredTypes);
    10. this.collect(attributes, "ignoredType", this.ignoredTypes);
    11. this.strategy = (SearchStrategy)metadata.getAnnotationAttributes(annotationType.getName()).get("search");
    12. OnBeanCondition.BeanTypeDeductionException deductionException = null;
    13. try {
    14. if (this.types.isEmpty() && this.names.isEmpty()) {
    15. this.addDeducedBeanType(context, metadata, this.types);
    16. }
    17. } catch (OnBeanCondition.BeanTypeDeductionException var7) {
    18. deductionException = var7;
    19. }
    20. this.validate(deductionException);
    21. }
    22. //验证的方法
    23. protected void validate(OnBeanCondition.BeanTypeDeductionException ex) {
    24. if (!this.hasAtLeastOne(this.types, this.names, this.annotations)) {
    25. String message = this.annotationName() + " did not specify a bean using type, name or annotation";
    26. if (ex == null) {
    27. throw new IllegalStateException(message);
    28. } else {
    29. throw new IllegalStateException(message + " and the attempt to deduce the bean's type failed", ex);
    30. }
    31. }
    32. }

    看一下OnBeanCondition类中的getMatchingBeans方法,里面有用到搜索策略,详见搜索策略介绍

    1. private List<String> getMatchingBeans(ConditionContext context, OnBeanCondition.BeanSearchSpec beans) {
    2. //获得当前bean工厂
    3. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    4. //判断当前的搜索策略是否是PARENTS或者ANCESTORS,默认是ALL
    5. if (beans.getStrategy() == SearchStrategy.PARENTS || beans.getStrategy() == SearchStrategy.ANCESTORS) {
    6. BeanFactory parent = beanFactory.getParentBeanFactory();
    7. Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.PARENTS");
    8. //如果是PARENTS或者ANCESTORS,当前bean工厂就用父工厂
    9. beanFactory = (ConfigurableListableBeanFactory)parent;
    10. }
    11. if (beanFactory == null) {
    12. return Collections.emptyList();
    13. } else {
    14. List<String> beanNames = new ArrayList();
    15. //如果当前搜索策略等于CURRENT,为true
    16. boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
    17. //这里的type就是需要查找的bean的类型
    18. //下面,会从属性中找bean
    19. Iterator var6 = beans.getTypes().iterator();
    20. String beanName;
    21. while(var6.hasNext()) {
    22. beanName = (String)var6.next();
    23. //如果找到了类型,接下来就是根据类型找bean的实例名,找示例名的方法在下方,实际上就是一个getNamesForType
    24. beanNames.addAll(this.getBeanNamesForType(beanFactory, beanName, context.getClassLoader(), considerHierarchy));
    25. }
    26. var6 = beans.getIgnoredTypes().iterator();
    27. while(var6.hasNext()) {
    28. beanName = (String)var6.next();
    29. beanNames.removeAll(this.getBeanNamesForType(beanFactory, beanName, context.getClassLoader(), considerHierarchy));
    30. }
    31. var6 = beans.getAnnotations().iterator();
    32. while(var6.hasNext()) {
    33. beanName = (String)var6.next();
    34. beanNames.addAll(Arrays.asList(this.getBeanNamesForAnnotation(beanFactory, beanName, context.getClassLoader(), considerHierarchy)));
    35. }
    36. var6 = beans.getNames().iterator();
    37. while(var6.hasNext()) {
    38. beanName = (String)var6.next();
    39. if (this.containsBean(beanFactory, beanName, considerHierarchy)) {
    40. beanNames.add(beanName);
    41. }
    42. }
    43. //将存放bean实例名的list返回
    44. return beanNames;
    45. }
    46. }
    47. //根据类型获取bean的name
    48. private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory, String type, ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
    49. try {
    50. Set<String> result = new LinkedHashSet();
    51. this.collectBeanNamesForType(result, beanFactory, ClassUtils.forName(type, classLoader), considerHierarchy);
    52. return result;
    53. } catch (ClassNotFoundException var6) {
    54. return Collections.emptySet();
    55. } catch (NoClassDefFoundError var7) {
    56. return Collections.emptySet();
    57. }
    58. }
    59. private void collectBeanNamesForType(Set<String> result, ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
    60. result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
    61. if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
    62. BeanFactory parent = ((HierarchicalBeanFactory)beanFactory).getParentBeanFactory();
    63. if (parent instanceof ListableBeanFactory) {
    64. this.collectBeanNamesForType(result, (ListableBeanFactory)parent, type, considerHierarchy);
    65. }
    66. }
    67. }

    找完bean了之后,回到刚才的代码里:

    1. //如果当前注入的bean是@ConditionalOnMissingBean
    2. if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
    3. //返回一个spec(说明),这里的spec规定了搜索的内容,比如搜索策略、需要搜索的类名......
    4. spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
    5. matching = this.getMatchingBeans(context, spec);
    6. if (!matching.isEmpty()) {
    7. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching));
    8. }
    9. matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, new Object[]{spec}).didNotFind("any beans").atAll();
    10. }

    如果第5行返回的list不是空的,就会返回ConditionOutcome对象noMatch方法,表示不匹配。ConditionOutcome类用于存放过滤结果,只有两个变量:

    1. /**
    2. * 过滤结果类
    3. */
    4. public class ConditionOutcome {
    5. /**
    6. * 匹配结果 true or false
    7. */
    8. private final boolean match;
    9. /**
    10. * 匹配结果信息
    11. */
    12. private final ConditionMessage message;

    两者区别:

    @ConditionOnBean在判断list的时候,如果list没有值,返回false,否则返回true

    @ConditionOnMissingBean在判断list的时候,如果list没有值,返回true,否则返回false,其他逻辑都一样

    例子:

    • @ConditionalOnBean(javax.sql.DataSource.class)    
      Spring容器或者所有父容器中需要存在至少一个javax.sql.DataSource类的实例
       

     

     

  • 相关阅读:
    第十九章:UTC time和local time的互换
    第二十章:安全性
    第十三章:基于socket.io实现即时通信
    第三章:ionic环境搭建之windows篇
    第十八章:自定义splash screen、app icon和tab icon
    第十七章:使用media插件来播放声音
    第一章:hybrid app开发之技术选型
    第十六章:自定义push notification sound
    第十五章:集成JPUSH
    ionic resources
  • 原文地址:https://www.cnblogs.com/jpfss/p/10870441.html
Copyright © 2020-2023  润新知