SpringBoot封装了很多基于Spring Framework中的Conditional类的实现。
如@ConditionalOnClass,@ConditionalOnBean …等等
这些注解是从何而来的呢?
关于Spring Framework中的Conditional
public @interface Conditional { Class<? extends Condition>[] value(); }
内部需要一个继承自Condition类的实现来判断是否将该Bean注入到Spring容器中
public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
其中matches方法用于判断是否将该Bean注入到Spring容器中,当matches方法返回true则将该Bean注入到Spring容器中
关于SpringBootCondition(所有SpringBoot条件注解的根)
public abstract class SpringBootCondition implements Condition { 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(); } 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); } } }
SpringBootCondition实现了Condition接口,并为matches方法定义了一个判断的框架。
其中matches方法如上:
getClassOrMethodName()获取条件注解写在了哪个类或者哪个方法上
ConditionOutcome outcome = this.getMatchOutcome(context, metadata);
该方法用于获取条件的判断结果。其中getMatchOutcome是一个抽象方法,留给子类去实现。
ConditionOutcome中包含boolean的match属性和ConditionMessage message属性。用于记录条件判断的信息。
this.logOutcome(classOrMethodName, outcome); 用于日志记录
this.recordEvaluation(context, classOrMethodName, outcome);
将判断结果记录到ConditionEvalutionReport中
ConditionEvalutionReportLoggingListener会在收到ContextRefreshedEvent事件后把匹配结果用日志打印出来
关于ConditionalOnClass
该注解的作用是当某个特定的类存在时,该判断为true,如SpringBoot内嵌容器中,如果Tomcat的某个类存在,则默认容器为Tomcat
@Conditional({OnClassCondition.class}) public @interface ConditionalOnClass { Class<?>[] value() default {}; String[] name() default {}; }
ConditionOnClass是一个组合注解,该注解是由SpringFramework 中Conditional注解标注,所以OnClassCondition必须是继承自Condition类的。
OnClassCondition类定义了该ConditionalOnClass的匹配逻辑。
value属性定义了Class类型的值,是一个数组.如果该Class在项目中存在,则判断为true
name属性Class类的全路径地址,如果ClassLoader加载该类成功,则判断为true
关于OnClassCondition
SpringBootCondition --> FilteringSpringBootCondition --> OnClassCondition
上面分析过SpringBootCondition的匹配逻辑,这里着重看OnClassCondition重载SpringBootCondition的中关于匹配的逻辑方法 ``getMatchOutcome`
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ClassLoader classLoader = context.getClassLoader(); ConditionMessage matchMessage = ConditionMessage.empty(); //拿到ConditionalOnClass注解中的value值,要判断是否存在的类名 List<String> onClasses = this.getCandidates(metadata, ConditionalOnClass.class); List onMissingClasses; if (onClasses != null) { //判断OnClass中不存在的类 onMissingClasses = this.filter(onClasses, ClassNameFilter.MISSING, classLoader); //如果有类确实,则表示不匹配 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)); } //下面是ConditionalOnMissingClass的逻辑 onMissingClasses = this.getCandidates(metadata, ConditionalOnMissingClass.class); if (onMissingClasses != null) { List<String> present = this.filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader); 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); }
List onClasses = this.getCandidates(metadata, ConditionalOnClass.class);
拿到ConditionalOnClass注解中的value值,要判断是否存在的类名
onMissingClasses = this.filter(onClasses, ClassNameFilter.MISSING, classLoader);
判断OnClass中不存在的类
如果判断某个类不存在,filter方法
protected final List<String> filter(Collection<String> classNames, 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(); if (classNameFilter.matches(candidate, classLoader)) { matches.add(candidate); } } return matches; } }
这里ClassNameFilter传入的是Missing,在ClassNameFilter源码中,主要通过isPresent判断
protected static enum ClassNameFilter { PRESENT { public boolean matches(String className, ClassLoader classLoader) { return isPresent(className, classLoader); } }, MISSING { public boolean matches(String className, ClassLoader classLoader) { return !isPresent(className, classLoader); } }; private ClassNameFilter() { } abstract boolean matches(String className, ClassLoader classLoader); static boolean isPresent(String className, ClassLoader classLoader) { if (classLoader == null) { classLoader = ClassUtils.getDefaultClassLoader(); } try { FilteringSpringBootCondition.resolve(className, classLoader); return true; } catch (Throwable var3) { return false; } } }
resoleve方法如下
protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException { return classLoader != null ? Class.forName(className, false, classLoader) : Class.forName(className); }
可以看到isPresent的逻辑是通过FilteringSpringBootCondition.resolve(className, classLoader); 来尝试加载该类,如果能正常加载,则代表该类存在,如果不能则代表该类不存在。