注解@ConditionalOnClass的源码实现
在Springboot中,支持了很多条件注解,@ConditionalOnClass注解就是其中之一。它主要是用来判断该注解所指定的某个类或者某些类是否在ClassPath中存在,如果存在则符合条件,如果不存在则不符合 。
注解源码:
1 2 3 4 5 6 7 8 @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass { Class<?>[] value() default {}; String[] name() default {}; }
通过value
和name
来指定要判断的类,而真正执行判断的逻辑在OnClassCondition
类中。
ConditionalOnClass
注解逻辑在OnClassCondition
类中
OnClassCondition
继承了SpringBootCondition
类
SpringBootCondition
类实现了Condition
接口
所以Spring在解析条件注解时,就会调用Condition
接口的matches()
方法,SpringBootCondition类中实现了matches()
方法,所以会被先调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public abstract class SpringBootCondition implements Condition { private final Log logger = LogFactory.getLog(getClass()); @Override public final boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { String classOrMethodName = getClassOrMethodName(metadata); try { ConditionOutcome outcome = getMatchOutcome(context, metadata); logOutcome(classOrMethodName, outcome); recordEvaluation(context, classOrMethodName, outcome); return outcome.isMatch(); } catch (NoClassDefFoundError ex) { throw new IllegalStateException( "Could not evaluate condition on " + classOrMethodName + " due to " + ex.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)" , ex); } catch (RuntimeException ex) { throw new IllegalStateException( "Error processing condition on " + getName(metadata), ex); } } }
在matches方法中,会调用getMatchOutcome()
方法,并得到ConditionOutcome
对象,它表示的就是条件判断的结果。
1 2 3 4 5 6 7 8 public class ConditionOutcome { private final boolean match; private final ConditionMessage message; }
getMatchOutcome()
方法在SpringBootCondition
类中是一个抽象方法,在子类OnClassCondition
类中才真正实现了getMatchOutcome
方法 进行条件判断.
所以核心就是这个getMatchOutcome()
方法,在这个方法中会先获取@ConditionalOnClass
注解的value和name属性的值,这些值就是待判断的类名集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @Order(Ordered.HIGHEST_PRECEDENCE) class OnClassCondition extends SpringBootCondition implements AutoConfigurationImportFilter , BeanFactoryAware , BeanClassLoaderAware { @Override public ConditionOutcome getMatchOutcome (ConditionContext context, AnnotatedTypeMetadata metadata) { ClassLoader classLoader = context.getClassLoader(); ConditionMessage matchMessage = ConditionMessage.empty(); List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class); if (onClasses != null ) { List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader); if (!missing.isEmpty()) { return ConditionOutcome .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) .didNotFind("required class" , "required classes" ) .items(Style.QUOTE, missing)); } matchMessage = matchMessage.andCondition(ConditionalOnClass.class) .found("required class" , "required classes" ).items(Style.QUOTE, getMatches(onClasses, MatchType.PRESENT, classLoader)); } List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class); if (onMissingClasses != null ) { List<String> present = getMatches(onMissingClasses, MatchType.PRESENT, classLoader); if (!present.isEmpty()) { return ConditionOutcome.noMatch( ConditionMessage.forCondition(ConditionalOnMissingClass.class) .found("unwanted class" , "unwanted classes" ) .items(Style.QUOTE, present)); } matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class) .didNotFind("unwanted class" , "unwanted classes" ).items(Style.QUOTE, getMatches(onMissingClasses, MatchType.MISSING, classLoader)); } return ConditionOutcome.match(matchMessage); } private List<String> getCandidates (AnnotatedTypeMetadata metadata, Class<?> annotationType) { MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true ); if (attributes == null ) { return Collections.emptyList(); } List<String> candidates = new ArrayList<>(); addAll(candidates, attributes.get("value" )); addAll(candidates, attributes.get("name" )); return candidates; } }
接下来就会逐个判断类名集合中的每个类名,判断逻辑为:利用MatchType.MISSING来判断某个类是否存在 。
1 List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
1 2 3 4 5 6 7 8 9 10 private List<String> getMatches (Collection<String> candidates, MatchType matchType, ClassLoader classLoader) { List<String> matches = new ArrayList<>(candidates.size()); for (String candidate : candidates) { if (matchType.matches(candidate, classLoader)) { matches.add(candidate); } } return matches; }
MatchType.MISSING就是利用ClassLoader
来加载类,如果加载到了表示类存在,没加载到表示类不存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private enum MatchType { PRESENT { @Override public boolean matches (String className, ClassLoader classLoader) { return isPresent(className, classLoader); } }, MISSING { @Override public boolean matches (String className, ClassLoader classLoader) { return !isPresent(className, classLoader); } }; private static boolean isPresent (String className, ClassLoader classLoader) { if (classLoader == null ) { classLoader = ClassUtils.getDefaultClassLoader(); } try { forName(className, classLoader); return true ; } catch (Throwable ex) { return false ; } } private static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException { if (classLoader != null ) { return classLoader.loadClass(className); } return Class.forName(className); } public abstract boolean matches (String className, ClassLoader classLoader) ; }
判断完之后,只要missing集合不为空,那就表示待判断的类中有不存在的,就返回条件不匹配的ConditionOutCome
对象,否则就返回条件匹配的ConditionOutCome
对象。