@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>
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7月前
|
NoSQL Java 关系型数据库
基于java swing和mysql实现的汽车租赁管理系统(源码+数据库+文档+运行指导视频)
基于java swing和mysql实现的汽车租赁管理系统(源码+数据库+文档+运行指导视频)
272 0
|
Java Spring
Eureka:com.netflix.discovery.TimedSupervisorTask - task supervisor timed out
Eureka:com.netflix.discovery.TimedSupervisorTask - task supervisor timed out
1572 0
|
安全 Java
一文教你,synchronized和Lock的区别?
最近有多位粉丝被问到synchronized和Lock,据说还是阿里一面的面试题。在分布式开发中,锁是控制线程的重要方式。Java提供了两种锁机制synchronized 和 Lock。接下来,我给大家分享一下我对synchronized和Lock的理解。
218 0
|
存储 关系型数据库 MySQL
MySQL的垂直分库分表
MySQL的垂直分库分表
232 0
|
Unix Linux
【Linux】chmod 命令详解
【Linux】chmod 命令详解
740 0
|
SQL Java 关系型数据库
【微服务 31】超细的Spring Cloud 整合Seata实现分布式事务(排坑版)
【微服务 31】超细的Spring Cloud 整合Seata实现分布式事务(排坑版)
2055 0
【微服务 31】超细的Spring Cloud 整合Seata实现分布式事务(排坑版)
|
7月前
|
前端开发 Java Spring
Spring Boot 实现图片上传并回显
Spring Boot 实现图片上传并回显
473 0
|
安全 Linux 数据安全/隐私保护
Centos虚拟机安装配置与MobaXterm工具及Linux常用命令(上)
Centos虚拟机安装配置与MobaXterm工具及Linux常用命令(上)
212 1
|
运维 大数据 数据挖掘
SelectDB x 白鲸开源金融大数据解决方案正式发布!
飞轮科技联合白鲸开源,针对金融行业大数据实时分析的场景共同推出高效实用的解决方案。
|
存储 NoSQL 算法
redis集群模式详解
redis集群模式详解
881 0