SpringBoot中ConditionalOnClass注解的原理
2020-05-12 16:03:15来源:博客园 阅读 ()
SpringBoot中ConditionalOnClass注解的原理
SpringBoot中的自动配置类有很多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注解标记的类加入到容器中
原文链接:https://www.cnblogs.com/hanabivvv/p/12876158.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- springboot2配置JavaMelody与springMVC配置JavaMelody 2020-06-11
- SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 2020-06-11
- 掌握SpringBoot-2.3的容器探针:实战篇 2020-06-11
- nacos~配置中心功能~springboot的支持 2020-06-10
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后 2020-06-10
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash