深入理解SpringBoot的过滤条件--AutoConfigure

简介: 揭开SpringBoot的spring-autoconfigure-metadata.properties的面纱

     我们知道在Spring及SpringBoot里按条件创建Bean的核心是Condition接口与Conditional注解,其实在SpringBoot里还有一种AutoConfigure也可以来过滤配置,只不过使用这种技术,能够让SpringBoot更快速的启动,那么下面我们就来看一下具体怎么实现的。

autoconfigure Module

     SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类。

演示示例

     在我们创建好的SpringBoot项目里添加一个AutoConfiguration:

package com.ys.zhshop.member.config;

import com.ys.zhshop.member.service.MemberRegisterService;
import org.springframework.context.annotation.Bean;

public class MemberAutoConfiguration {

    @Bean
    public MemberRegisterService registerService() {
        return new MemberRegisterService();
    }
}

     在MemberRegisterService里的构造函数输出一段内容看看Spring是否帮我们初始化
     紧接着在META-INF/spring.factories里配置对应的引导:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.ys.zhshop.member.config.MemberAutoConfiguration

     随后我们需要在META-INF目录下创建一个spring-autoconfigure-metadata.properties 文件,内容如下:

com.ys.zhshop.member.config.MemberAutoConfiguration.ConditionalOnClass=java.lang.Strings

     格式:自动配置的类全名.条件=值

     在这里我们先指定一个类路径下不存在的Java类,启动后并没有相关信息的输出,那么把其值改成java.land.String,那么我们启动可以发现:
415409F1_B9A5_4BAA_89EB_59931DCC4A75
     在这里,我们可以在控制台看到构造函数输出的值,这就说明我们的Bean的的确确被创建了
     下面我贴出一个spring-cloud-netflix-core下的配置,主要来看看这些条件该怎么写,大家如果想使用可以参考人家的来配置:

#Mon Jun 18 19:13:37 UTC 2018
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration.ConditionalOnClass=com.netflix.hystrix.Hystrix,org.springframework.boot.actuate.health.HealthIndicator
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration=
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration=
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration.Configuration=
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration=
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration.AutoConfigureAfter=org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration.Configuration=
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration.Configuration=
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration.ConditionalOnClass=com.netflix.hystrix.Hystrix,org.springframework.security.core.context.SecurityContext

     根据官网说法,使用这种配置方式可以有效的降低SpringBoot的启动时间,因为通过这种过滤方式能减少@Configuration类的数量,从而降低初始化Bean时的耗时,官网原话描述如下:

Spring Boot uses an annotation processor to collect the conditions on auto-configurations in a metadata file (META-INF/spring-autoconfigure-metadata.properties). If that file is present, it is used to eagerly filter auto-configurations that do not match, which will improve startup time.

源码追踪

     我们再次打开自动化配置的核心类AutoConfigurationImportSelector,看看它的selectImport方法:

     @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);//1
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);//2
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);//3
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }
  1. 在代码1处创建用于加载spring-autoconfigure-metadata.properties里的元数据,在这里我们可以看到其指定文件的位置:
protected static final String PATH = "META-INF/"
            + "spring-autoconfigure-metadata.properties";

2.在代码2处主要通过SpringFactoriesLoader加载spring.factories里的EnableAutoConfiguration
3.根据AutoConfigurationMetaData进行一次过滤:

private List<String> filter(List<String> configurations,
            AutoConfigurationMetadata autoConfigurationMetadata) {
        long startTime = System.nanoTime();
        String[] candidates = StringUtils.toStringArray(configurations);
        boolean[] skip = new boolean[candidates.length];
        boolean skipped = false;
        for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
            invokeAwareMethods(filter);
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);//代码1
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) {
                    skip[i] = true;
                    skipped = true;
                }
            }
        }
        if (!skipped) {
            return configurations;
        }
        List<String> result = new ArrayList<>(candidates.length);
        for (int i = 0; i < candidates.length; i++) {
            if (!skip[i]) {
                result.add(candidates[i]);
            }
        }
        if (logger.isTraceEnabled()) {
            int numberFiltered = configurations.size() - result.size();
            logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                    + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
                    + " ms");
        }
        return new ArrayList<>(result);
    }

我们在这里主要关注代码1。OnClassConditionAutoConfigurationImportFilter接口的实现类,我这里贴一下与主题相关的代码:

@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends SpringBootCondition
        implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {


@Override
    public boolean[] match(String[] autoConfigurationClasses,
            AutoConfigurationMetadata autoConfigurationMetadata) {
        ConditionEvaluationReport report = getConditionEvaluationReport();
        ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
                autoConfigurationMetadata);
        boolean[] match = new boolean[outcomes.length];
        for (int i = 0; i < outcomes.length; i++) {
            match[i] = (outcomes[i] == null || outcomes[i].isMatch());
            if (!match[i] && outcomes[i] != null) {
                logOutcome(autoConfigurationClasses[i], outcomes[i]);
                if (report != null) {
                    report.recordConditionEvaluation(autoConfigurationClasses[i], this,
                            outcomes[i]);
                }
            }
        }
        return match;
    }
//.....

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];
                Set<String> candidates = autoConfigurationMetadata
                        .getSet(autoConfigurationClass, "ConditionalOnClass");
                if (candidates != null) {
                    outcomes[i - start] = getOutcome(candidates);
                }
            }
            return outcomes;
        }
}

上述代码虽然多,但最终还是要调用AutoConfigurationMetadata 的getSet方法,我们继续追踪一下这个接口的实现类,它是位于AutoConfigurationMetadataLoader的内部类:


    /**
     * {@link AutoConfigurationMetadata} implementation backed by a properties file.
     */
    private static class PropertiesAutoConfigurationMetadata
            implements AutoConfigurationMetadata {
    
      //....省略其他代码

      @Override
        public Set<String> getSet(String className, String key) {
            return getSet(className, key, null);
        }

        @Override
        public Set<String> getSet(String className, String key,
                Set<String> defaultValue) {
            String value = get(className, key);
            return (value != null ? StringUtils.commaDelimitedListToSet(value)
                    : defaultValue);
        }

        @Override
        public String get(String className, String key) {
            return get(className, key, null);
        }

        @Override
        public String get(String className, String key, String defaultValue) {
            String value = this.properties.getProperty(className + "." + key);
            return (value != null ? value : defaultValue);
        }
  }

最后我们可以发现其get方法还是通过java.util.Properties的getProperty方法来获取的值

目录
相关文章
|
10天前
|
前端开发 安全 Java
基于springboot+vue开发的会议预约管理系统
一个完整的会议预约管理系统,包含前端用户界面、管理后台和后端API服务。 ### 后端 - **框架**: Spring Boot 2.7.18 - **数据库**: MySQL 5.6+ - **ORM**: MyBatis Plus 3.5.3.1 - **安全**: Spring Security + JWT - **Java版本**: Java 11 ### 前端 - **框架**: Vue 3.3.4 - **UI组件**: Element Plus 2.3.8 - **构建工具**: Vite 4.4.5 - **状态管理**: Pinia 2.1.6 - **HTTP客户端
91 4
基于springboot+vue开发的会议预约管理系统
|
4月前
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
442 1
|
5月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
420 7
|
1月前
|
前端开发 JavaScript Java
基于springboot+vue开发的校园食堂评价系统【源码+sql+可运行】【50809】
本系统基于SpringBoot与Vue3开发,实现校园食堂评价功能。前台支持用户注册登录、食堂浏览、菜品查看及评价发布;后台提供食堂、菜品与评价管理模块,支持权限控制与数据维护。技术栈涵盖SpringBoot、MyBatisPlus、Vue3、ElementUI等,适配响应式布局,提供完整源码与数据库脚本,可直接运行部署。
81 0
基于springboot+vue开发的校园食堂评价系统【源码+sql+可运行】【50809】
|
4月前
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
446 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
8月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的留守儿童爱心网站设计与实现(计算机毕设项目实战+源码+文档)
博主是一位全网粉丝超过100万的CSDN特邀作者、博客专家,专注于Java、Python、PHP等技术领域。提供SpringBoot、Vue、HTML、Uniapp、PHP、Python、NodeJS、爬虫、数据可视化等技术服务,涵盖免费选题、功能设计、开题报告、论文辅导、答辩PPT等。系统采用SpringBoot后端框架和Vue前端框架,确保高效开发与良好用户体验。所有代码由博主亲自开发,并提供全程录音录屏讲解服务,保障学习效果。欢迎点赞、收藏、关注、评论,获取更多精品案例源码。
|
8月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的家政服务管理平台设计与实现(计算机毕设项目实战+源码+文档)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
|
8月前
|
JavaScript 搜索推荐 Java
基于SpringBoot+Vue实现的家乡特色推荐系统设计与实现(源码+文档+部署)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
|
8月前
|
JavaScript NoSQL Java
基于SpringBoot+Vue实现的大学生就业服务平台设计与实现(系统源码+文档+数据库+部署等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
|
8月前
|
JavaScript Java 测试技术
基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!