zoukankan      html  css  js  c++  java
  • SpringBoot中ConditionalOnClass注解的原理

    SpringBoot中的自动配置类有很多ConditionalOnClass注解,@ConditionalOnClass 在注解值中所有的类都存在时(通过尝试使用类加载器加载指定的类的方式判断)才会匹配,

    那这些ConditionalOnClass注解的原理是什么呢,了解ConditionalOnClass注解的原理前要先了解Conditional注解的原理,因为Conditional注解是最基础的

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
        Class<? extends Condition>[] value();
    }

    Contional注解里可以标注的属性是一个Class数组

    而处理这个注解的是一个Condition接口,SpringBootCondition就实现了这个接口来对Contional注解进行处理

    @FunctionalInterface
    public interface Condition {
        boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
    }
    public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    String classOrMethodName = getClassOrMethodName(metadata);

    try {
    //获取ContionOutCome对象,这个对象的属性有是否匹配和匹配信息
    ConditionOutcome outcome = this.getMatchOutcome(context, metadata);//getMatchOutcome方法在本类是一个抽象方法,具体实现是在子类实现的,OnClassCondition就实现了这个方法
    this.logOutcome(classOrMethodName, outcome);
    this.recordEvaluation(context, classOrMethodName, outcome);
    return outcome.isMatch();//返回匹配结果 true或者是False
    } catch (NoClassDefFoundError var5) {
    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);
    } catch (RuntimeException var6) {
    throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
    }
    }

    了解完Conditional注解的原理之后就可以来了解ConditionalOnClass注解的原理了,可以先看下ConditionalOnClass注解的定义

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional({OnClassCondition.class})
    public @interface ConditionalOnClass {
        Class<?>[] value() default {};
    
        String[] name() default {};
    }

    可以看到ConditionalOnClass注解其实是通过Conditional注解起作用的,Conditional注解标注的属性是OnClassCondition.class,接着来看OnClassCondition.class的源码,从源码可以看到OnClassCondition.class是通过getMatchOutCome来获取匹配结果的,

    而匹配结果是在SpringBootCondition里被使用的

    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
              //获取容器的类加载器
            ClassLoader classLoader = context.getClassLoader();
            ConditionMessage matchMessage = ConditionMessage.empty();
        // 获取@ConditionalOnClass注解 value以及name属性声明的所有类
            List<String> onClasses = this.getCandidates(metadata, ConditionalOnClass.class);
            List onMissingClasses;
            if (onClasses != null) {
                //加载ConditionalOnClass注解指定的类
                onMissingClasses = this.filter(onClasses, ClassNameFilter.MISSING, classLoader);
                // 如果加载成功即类路径上有ConditionalOnClasses注解指定的类,也就是说onMissingClasses为空,加载失败即onMissingClasses不为空,返回一个匹配失败的结果
                if (!onMissingClasses.isEmpty()) {
                    return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class, new Object[0]).didNotFind("required class", "required classes").items(Style.QUOTE, onMissingClasses));
                }
              
                matchMessage = matchMessage.andCondition(ConditionalOnClass.class, new Object[0]).found("required class", "required classes").items(Style.QUOTE, this.filter(onClasses, ClassNameFilter.PRESENT, classLoader));
            }
              
            
            onMissingClasses = this.getCandidates(metadata, ConditionalOnMissingClass.class);
            if (onMissingClasses != null) {
                
                //加载ConditionalOnMissingClass注解指定的类
                List<String> present = this.filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
                // 如果加载失败即类路径上没有ConditionalOnMissingClasses注解指定的类,也就是说present为空,加载成功即present不为空,返回一个匹配失败的结果
                if (!present.isEmpty()) {
                    return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class, new Object[0]).found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
                }
    
                matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class, new Object[0]).didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, this.filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
            }
    
            return ConditionOutcome.match(matchMessage);//返回成功匹配的结果
        }

    public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    String classOrMethodName = getClassOrMethodName(metadata);

    try {
    ConditionOutcome outcome = this.getMatchOutcome(context, metadata);//上面方法返回的匹配结果是在这里使用的
    this.logOutcome(classOrMethodName, outcome);
    this.recordEvaluation(context, classOrMethodName, outcome);
    return outcome.isMatch();//匹配成功则返回true,把ConditionalOnClass标记的类加入容器中,匹配失败则跳过标注的类
    } catch (NoClassDefFoundError var5) {
    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);
    } catch (RuntimeException var6) {
    throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
    }
    }
     

    可以看出getMatchOutCome方法里是通过filter方法来判断类路径上是否有ConditionalOnClass注解里标注的类

    protected final List<String> filter(Collection<String> classNames, FilteringSpringBootCondition.ClassNameFilter classNameFilter, ClassLoader classLoader) {
            if (CollectionUtils.isEmpty(classNames)) {
                return Collections.emptyList();
            } else {
                List<String> matches = new ArrayList(classNames.size());
                Iterator var5 = classNames.iterator();
    
                while(var5.hasNext()) {
                    String candidate = (String)var5.next();
                    //classNameFilter为MISSING时,如果加载到了 
                    //ConditionalOnClass指定的类,则matches的返回值为flase,则不会把ConditionalOnClass注解的属性加入到matches,即matches为空
                    if (classNameFilter.matches(candidate, classLoader)) {
                        matches.add(candidate);
                    }
                }
    
                return matches;
            }
        }
    filter方法是通过ClassNameFilter这个枚举类里的matches方法来判断类路径上是否有ConditionalOnClass注解里标注的类
    protected static enum ClassNameFilter {
        //返回类路径中是否存在该类,该类能被加载则返回True,否则返回False
            PRESENT {
                public boolean matches(String className, ClassLoader classLoader) {
                    return isPresent(className, classLoader);
                }
            },
        //返回类路径中是否不存在该类,该类能被加载则返回False,否则返回True
            MISSING {
                public boolean matches(String className, ClassLoader classLoader) {
                    return !isPresent(className, classLoader);
                }
            };
    
            private ClassNameFilter() {
            }
    
            abstract boolean matches(String var1, ClassLoader var2);
    
            static boolean isPresent(String className, ClassLoader classLoader) {
                if (classLoader == null) {
                    classLoader = ClassUtils.getDefaultClassLoader();
                }
    
                try {
                    //利用loadClass以及forName方法,判断类路径下有没有这个指定的类
                    //有则返回true
                    FilteringSpringBootCondition.resolve(className, classLoader);
                    return true;
                } catch (Throwable var3) {
                    //没有则加载失败返回false
                    return false;
                }
            }
        }
    }

    protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
    return classLoader != null ? classLoader.loadClass(className) : Class.forName(className);//有类加载器就通过类加载器加载,没有则通过forName方法加载
    }

    看到这里我们就可以知道ConditionalOnClass注解里的类属性是通过类加载器和forName的方法判断类路径上是否有该类,如果类路径上有该类则加载成功,也就是能够成功匹配,成功匹配后SpringBoot就会把ConditionalOnClass注解标记的类加入到容器中



    
    
  • 相关阅读:
    array_map()与array_shift()搭配使用 PK array_column()函数
    Educational Codeforces Round 8 D. Magic Numbers
    hdu 1171 Big Event in HDU
    hdu 2844 poj 1742 Coins
    hdu 3591 The trouble of Xiaoqian
    hdu 2079 选课时间
    hdu 2191 珍惜现在,感恩生活 多重背包入门题
    hdu 5429 Geometric Progression 高精度浮点数(java版本)
    【BZOJ】1002: [FJOI2007]轮状病毒 递推+高精度
    hdu::1002 A + B Problem II
  • 原文地址:https://www.cnblogs.com/hanabivvv/p/12876158.html
Copyright © 2011-2022 走看看