写点什么

@ConditionOnClass 的使用

用户头像
Rubble
关注
发布于: 3 小时前
@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 Filtersorg.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></dependency>
复制代码


用户头像

Rubble

关注

还未添加个人签名 2021.06.01 加入

还未添加个人简介

评论

发布
暂无评论
@ConditionOnClass的使用