@ConditionOnClass的使用

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: @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>
相关文章
|
NoSQL Java 关系型数据库
基于java swing和mysql实现的汽车租赁管理系统(源码+数据库+文档+运行指导视频)
基于java swing和mysql实现的汽车租赁管理系统(源码+数据库+文档+运行指导视频)
498 0
|
人工智能 监控 数据可视化
Java智慧工地可视化APP信息管理平台源码
利用物联网、云计算等先进信息化技术手段,实时掌握施工工地全方位的现场情况,提高数据获取的准确性、及时性、真实性和响应速度。
148 3
|
9月前
|
数据采集 存储 缓存
如何使用缓存技术提升Python爬虫效率
如何使用缓存技术提升Python爬虫效率
|
9月前
|
前端开发 搜索推荐 安全
如何选择CMS网站管理系统?CMS包含哪些功能?
本文主要介绍了CMS(内容管理系统)的概念,以及其在网站管理中的应用。早期的CMS主要以静态HTML+CSS+JS展示,但是随着互联网的发展,功能越来越丰富,如会员管理、多元素素材上传等。
472 3
|
前端开发 C++
CSS【详解】 标准盒模型 VS IE 盒模型
CSS【详解】 标准盒模型 VS IE 盒模型
181 0
|
开发者 Python
确保你的Python环境中已经安装了`python-docx`模块。如果还没有安装,可以通过pip来安装:
确保你的Python环境中已经安装了`python-docx`模块。如果还没有安装,可以通过pip来安装:
|
消息中间件 存储 监控
扒开kafka内部组件,咱瞅一瞅都有啥?
以上是 V 哥整理的关于 Kafka 核心组件的介绍,掌握 Kafka 中间件,应用在大型分布式项目中,这对于人个的项目经验积累是浓墨重彩的笔,换句话说,只要是有用到Kafka 的项目,必然是小不了,否则架构师脑袋长泡了。
244 1
|
存储 NoSQL 安全
红包系统架构设计
红包系统架构设计
2096 0
红包系统架构设计
|
C++
选择结构与条件判断:编程中的决策利器
在编程中,选择结构和条件判断是不可或缺的一部分。它们允许程序根据特定的条件来执行不同的代码块,从而实现程序的决策功能。本文将深入探讨选择结构和条件判断的概念,并通过示例代码展示其在实际编程中的应用。
211 2
R|tableone 快速绘制文章“表一”-基线特征三线表
R|tableone 快速绘制文章“表一”-基线特征三线表
316 0