SpringBoot条件注解ConditionalOnClass底层原理

最近使用了ConditionalOnClass注解,但是不知道底层是怎么实现的,趁周末看一下,顺便进行记录。


conditionOnClass.png

先来看一下结构:核心是OnClassCondition这个类,最上层实现了Condition接口。最后判断是否存在这个类,是使用的ClassLoader.loadClass()方法。

然后看@ConditionalOnClass注解源码

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
  Class[] value() default {}; // 需要匹配的类
  String[] name() default {}; // 需要匹配的类名
}

它有2个属性,分别是类数组和字符串数组(作用一样,类型不一样),而且被@Conditional注解所修饰,这个@Conditional注解有个名为values的Class< ? extends Condition >[]类型的属性。 这个Condition是个接口,用于匹配组件是否有资格被容器注册,定义如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

    /**
     * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class[] value();

}


@FunctionalInterface
public interface Condition {

    /**
     * Determine if the condition matches.
     * @param context the condition context
     * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
     * or {@link org.springframework.core.type.MethodMetadata method} being checked
     * @return {@code true} if the condition matches and the component can be registered,
     * or {@code false} to veto the annotated component's registration
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

也就是说@Conditional注解属性中可以持有多个Condition接口的实现类,所有的Condition接口需要全部匹配成功后这个@Conditional修饰的组件才有资格被注册。

Condition接口有个实现抽象类SpringBootCondition,SpringBoot中所有条件注解对应的条件类都继承这个抽象类。它实现了matches方法:

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);
        }
    }
}

然后提供了一个抽象方法getMatchOutcome等待子类去实现。

SpringBoot提供了两个基于Class的条件注解:@ConditionalOnClass(类加载器中存在指明的类)或者@ConditionalOnMissingClass(类加载器中不存在指明的类)。

@ConditionalOnClass或者@ConditionalOnMissingClass注解对应的条件类是OnClassCondition,定义如下:

先看实现的getMatchOutcome方法

@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        //获取上下文中的classLoader
        ClassLoader classLoader = context.getClassLoader();
        ConditionMessage matchMessage = ConditionMessage.empty();
        //获取加ConditionalOnClass注解的所有类名
        List onClasses = getCandidates(metadata, ConditionalOnClass.class);
        if (onClasses != null) {
            //进行过滤,找到上面onClasses这个list中使用当前的classLoader获取不到的类
            List missing = filter(onClasses, ClassNameFilter.MISSING,
                    classLoader);
            //如果不为空,返回一个匹配失败的ConditionOutcome结果,
            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,
                            filter(onClasses, ClassNameFilter.PRESENT, classLoader));
        }
    //下面同理,找ConditionalOnMissingClass这个注解的匹配结果
        List onMissingClasses = getCandidates(metadata,
                ConditionalOnMissingClass.class);
        if (onMissingClasses != null) {
            List present = filter(onMissingClasses, ClassNameFilter.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, filter(onMissingClasses, ClassNameFilter.MISSING,
                            classLoader));
        }
        return ConditionOutcome.match(matchMessage);
    }


}

最后看一下是如何匹配的,在List missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);方法

    protected List filter(Collection classNames,
            ClassNameFilter classNameFilter, ClassLoader classLoader) {
        if (CollectionUtils.isEmpty(classNames)) {
            return Collections.emptyList();
        }
        List matches = new ArrayList<>(classNames.size());
        for (String candidate : classNames) {
            //ClassNameFilter枚举中的matches方法
            if (classNameFilter.matches(candidate, classLoader)) {
                matches.add(candidate);
            }
        }
        return matches;
    }
    
    protected enum ClassNameFilter {

        PRESENT {

            @Override
            public boolean matches(String className, ClassLoader classLoader) {
                return isPresent(className, classLoader);
            }

        },

        MISSING {

            @Override
            public boolean matches(String className, ClassLoader classLoader) {
                //走的是isPresent
                return !isPresent(className, classLoader);
            }

        };

        public abstract boolean matches(String className, ClassLoader classLoader);
        //最底层的方法是下面的forName,调用的ClassLoader.loadClass
        public 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);
        }

    }

    
    

你可能感兴趣的:(SpringBoot条件注解ConditionalOnClass底层原理)