欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot中ConditionalOnClass注解的原理

程序员文章站 2022-07-04 18:37:37
SpringBoot中的自动配置类有很多ConditionalOnClass注解,@ConditionalOnClass 在注解值中所有的类都存在时(通过尝试使用类加载器加载指定的类的方式判断)才会匹配, 那这些ConditionalOnClass注解的原理是什么呢,可以先看下Conditional ......

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

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

@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方法在本类是一个抽象方法,具体实现是在子类实现的,oncontionalclass就实现了这个方法
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);
}
}

 

了解了condition注解的原理之后就可以来了解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);
            // 如果加载失败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注解标记的类加入到

容器中