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注解标记的类加入到容器中



    
    
  • 相关阅读:
    linux学习笔记三
    linux学习笔记二
    linux学习笔记一
    Linux操作篇之配置DNS服务(二)
    Linux操作篇之配置DNS服务(一)
    Linux操作篇之配置DHCP服务
    Linux操作篇之配置SSH服务
    Linux操作篇之自动化安装操作系统(二)
    Linux操作篇之自动化安装操作系统(一)
    Linux的shell编程篇之环境变量配置文件
  • 原文地址:https://www.cnblogs.com/hanabivvv/p/12876158.html
Copyright © 2011-2022 走看看