@ConditionOnClass的使用

SpringApplication.run的加载过程通过选择器AutoConfigurationImportSelector进行自动配置加载。

AutoConfigurationImportSelector类process处理过程获取了所有的配置configurations,然后进行filter过滤。

/** class AutoConfigurationImportSelector **/
// 使用DeferredImportSelector 处理SpringBootApplication注解
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
          // 获取配置实体  getAutoConfigurationMetadata 获取自动配置元数据
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }
/**  class AutoConfigurationImportSelector **/
// 获取自动配置实体
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //对配置类进行过滤
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
/** class AutoConfigurationImportSelector **/
// 获取自动配置元数据
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
 if (this.autoConfigurationMetadata == null) {
  this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
 }
 return this.autoConfigurationMetadata;
}

/** class AutoConfigurationMetadataLoader **/
final class AutoConfigurationMetadataLoader {
    
    protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
  
    // 从配置文件 META-INF/spring-autoconfigure-metadata.properties 加载元数据
    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, PATH);
    }
}

AutoConfigurationMetadataLoader类从META-INF/spring-autoconfigure-metadata.properties加载元数据。

RedisAutoConfiguration.ConditionalOnClass

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.ConditionalOnClass=org.springframework.data.redis.core.RedisOperations

继续看filter方法

从配置文件中加载了AutoConfigurationImportFilter的配置类,使用所有类进行匹配过滤。

每个配置类使用了AutoConfigurationImportFilter的match方法进行匹配,继而调用了实现类的getOutcomes进行检验。

private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {

 String[] candidates = StringUtils.toStringArray(configurations);
 ...
 for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
  invokeAwareMethods(filter);
  boolean[] match = filter.match(candidates, autoConfigurationMetadata);
  ...
 }
 ...
}

getAutoConfigurationImportFilters() 方法使用SpringFactoriesLoader从配置文件spring.factories中加载AutoConfigurationImportFilter自动过滤类

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
 return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
# spring.factories
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

FilteringSpringBootCondition类的继承关系

FilteringSpringBootCondition (org.springframework.boot.autoconfigure.condition)

|--OnBeanCondition (org.springframework.boot.autoconfigure.condition)

|--OnClassCondition (org.springframework.boot.autoconfigure.condition)

|--OnWebApplicationCondition (org.springframework.boot.autoconfigure.condition)

来看下 OnClassCondition类条件的匹配

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

 @Override
 protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
   AutoConfigurationMetadata autoConfigurationMetadata) {
  // Split the work and perform half in a background thread if more than one
  // processor is available. Using a single additional thread seems to offer the
  // best performance. More threads make things worse.
   // 多核处理器,使用一个额外的线程处理一半工作,以获得最优性能
  if (Runtime.getRuntime().availableProcessors() > 1) {
   return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
  }
  else {
   OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0,
     autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
   return outcomesResolver.resolveOutcomes();
  }
 }

OnClassCondition类获取匹配信息 通过key ConditionalOnClass来获取配置文件的类, matches通过classloader进行加载类,不存在类则添加异常信息

/** class OnClassCondition **/
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
                AutoConfigurationMetadata autoConfigurationMetadata) {
            ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
            for (int i = start; i < end; i++) {
                String autoConfigurationClass = autoConfigurationClasses[i];
                if (autoConfigurationClass != null) {
                    String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
                    if (candidates != null) {
                        outcomes[i - start] = getOutcome(candidates);
                    }
                }
            }
            return outcomes;
        }

ConditionalOnClass注解匹配信息

/** class OnClassCondition **/
private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
 if (ClassNameFilter.MISSING.matches(className, classLoader)) {
  return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
    .didNotFind("required class").items(Style.QUOTE, className));
 }
 return null;
}

FilteringSpringBootCondition 使用classLoader进行类加载

/** class FilteringSpringBootCondition **/
protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
 if (classLoader != null) {
  return classLoader.loadClass(className);
 }
 return Class.forName(className);
}

[](#有趣的写法)有趣的写法

enum实现类继承

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) {
   return !isPresent(className, classLoader);
  }

 };

 abstract boolean matches(String className, ClassLoader classLoader);

 static boolean isPresent(String className, ClassLoader classLoader) {
  if (classLoader == null) {
   classLoader = ClassUtils.getDefaultClassLoader();
  }
  try {
   resolve(className, classLoader);
   return true;
  }
  catch (Throwable ex) {
   return false;
  }
 }

}

[](#总结)总结

SpringBoot自动配置过程AutoConfigurationImportSelector选择器process处理过程,通过filter进行配置过滤处理,filter是从配置文件spring.factories中获取FilteringSpringBootCondition配置类(OnBeanCondition、OnClassCondition、OnWebApplicationCondition),OnClassCondition实现对ConditionalOnClass的处理,springBoot从配置文件spring-autoconfigure-metadata.properties获取的元数据autoConfigurationMetadata中获取key为ConditionalOnClass的配置类,通过ClassLoader进行类加载,类不存在则过滤掉不再进行配置类加载。

那么@ConditionOnClass编译是怎么处理的呢?

使用optional选项

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-redis</artifactId>
  <version>2.2.5.RELEASE</version>
  <scope>compile</scope>
  <optional>true</optional>
上一篇:自定义一个spring自动配置类


下一篇:大数据网红发现神器