【SpringBoot】SpringBoot的自动配置源码解析

这篇具有很好参考价值的文章主要介绍了【SpringBoot】SpringBoot的自动配置源码解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. SpringBoot的自动配置概念

SpringBoot相对于SSM来说,主要的优点就是简化了配置,不再需要像SSM哪有写一堆的XML配置,这些XML配置在大项目上会成为一种累赘,使得后期项目难以维护。

SpringBoot的出现,使得开发者不再关注于配置,能够更加专注于业务的开发,这得益于SpringBoot的自动配置。


2. SpringBoot自动配置的原理

SpringBoot的自动配置的核心就在于SpringBoot启动类中的@SpringBootApplication注解上

@SpringBootApplication
@Slf4j
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
        log.info("Project running .....");
    }

}

这是一个复合注解,标识该类为SpringBoot的应用入口,里面包含了SpringBootConfigurationEnableAutoConfigurationComponentScan三个注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
	....
}

三个注解的作用如下

  1. SpringBootConfiguration:这是SpringBoot框架中的一个特殊注解,属于@Configuration派生注解,它的作用是在应用启动时会被自动加载和处理,简化应用程序的配置过程,提供快速的启动配置.
  2. EnableAutoConfiguration:启用自动配置机制(自动配置的核心)
  3. ComponentScan:扫描启动类路径下的类,自动注册带有@Component以及其他相关注解的类到Spring容器中

3. EnableAutoConfiguration

EnableAutoConfiguration翻译过来就是开启自动配置,说明这个类的作用就是开启自动配置的作用。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

EnableAutoConfiguration也是一个复合注解,其中AutoConfigurationPackage注解的作用是将当前类所在的包以及子包作为自动配置的包路径,以便让SpringBoot能够自动加载和处理这些组件的配置。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

AutoConfigurationPackage中有@Import({AutoConfigurationPackages.Registrar.class})

@Import({AutoConfigurationPackages.Registrar.class})的作用是导入一个配置文件,在springboot中为给容器导入一个组件,而导入的组件由 AutoConfigurationPackages.class的内部类Registrar.class 执行逻辑来决定是如何导入的。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
    }

    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }
}

Registrar作为一个静态内部类,实现了ImportBeanDefinitionRegistrar接口,就可以被@Import注入到Spring容器中。

在这里类中,需要重点关注registerBeanDefinitions方法

【SpringBoot】SpringBoot的自动配置源码解析,SpringBoot,spring boot,java,spring

通过DEBUG发现,(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])的值为当前启动类所在的包名。

因此,可以得出结论,这个方法的主要作用是启动类所在的包下的所有组件注入到Spring容器中。

接着,再看@Import({AutoConfigurationImportSelector.class})

通过@ImportAutoConfigurationImportSelector选择器导入。

AutoConfigurationImportSelector需要重点关注getAutoConfigurationEntry方法

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        // 检查是否开始自动配置,如果返回false,则不开启,不需要导入任何配置类
        return EMPTY_ENTRY;
    } else {
        //从注解元数据中获取自动配置相关属性
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        //根据注解元数据和属性,获取潜在的候选自动配置类的列表
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        //去除重复的自动配置类
        configurations = this.removeDuplicates(configurations);
        //获取与自动配置相关的排除列表,即需要从候选自动配置中排除的类。
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        //检查排除列表中的类是否存在于候选自动配置列表中,并进行必要的处理。
        this.checkExcludedClasses(configurations, exclusions);
        //从候选自动配置列表中移除在排除列表中指定的类。
        configurations.removeAll(exclusions);
        //使用配置类过滤器(ConfigurationClassFilter)对候选自动配置列表进行进一步筛选和过滤。
        configurations = this.getConfigurationClassFilter().filter(configurations);
        //触发自动配置导入事件,可以通知其他监听器关于自动配置的导入信息。
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

这个方法的返回值是候选的配置类,经过处理和筛选后的自动配置类列表以及排除列表。

重点是关注如何获取自动配置类列表,这个需要关注getCandidateConfigurations方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //使用 SpringFactoriesLoader 从 META-INF/spring.factories 文件中加载所有的工厂名称,并将它们存储在一个新的 ArrayList 中。
    //getSpringFactoriesLoaderFactoryClass() 返回用于加载自动配置的工厂类。
    List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
    //使用 ImportCandidates 从指定的类路径加载 AutoConfiguration 类的子类或实现类,并将它们添加到候选配置列表中。
    //this.getBeanClassLoader() 返回当前线程的类加载器。
    ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

这个方法的主要作用就是利用SpringFactoriesLoader加载META-INF/spring.factories文件

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    //配置类加载器
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
	
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

接着查看loadSpringFactories,这里面就是获取需要配置的类的主要核心过程了。

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    //首先从缓存中获取给定类加载器(classLoader)对应的工厂配置信息。如果缓存中已存在,则直接返回结果。
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        //用于存储工厂配置信息。
        Map<String, List<String>> result = new HashMap();

        try {
            //使用给定的类加载器获取所有位于 META-INF/spring.factories 路径下的资源文件的 URL 枚举
            Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
			//遍历每个资源文件的 URL
            while(urls.hasMoreElements()) {
                //获取下一个资源文件的 URL
                URL url = (URL)urls.nextElement();
                //将 URL 封装为 UrlResource 对象,以便进行读取。
                UrlResource resource = new UrlResource(url);
                //通过 PropertiesLoaderUtils 加载 UrlResource 对象所代表的资源文件,并将其作为属性对象 Properties 进行读取。
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();
				//遍历每个属性条目。
                while(var6.hasNext()) {
                    Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                    //提取工厂类型名称,并进行去除首尾空格的处理。
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    //将工厂实现类名按逗号分隔,转换为字符串数组。
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        //将工厂实现类名添加到对应的工厂类型键下的列表中。如果该键不存在,则创建一个新的空列表。
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }
			//对结果中的每个工厂类型及其对应的实现类列表,进行去重操作,并将列表转换为不可修改的集合,以确保唯一性
            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            //将结果存储到缓存中,以便下次直接获取
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

也就是说这个方法通过加载和解析 META-INF/spring.factories 文件中的内容,将工厂类型和其对应的实现类名关联起来,并返回一个包含工厂配置信息的 Map 对象。它使用了缓存机制来避免重复加载相同的配置文件,提高了性能。这些工厂配置信息在 Spring Boot 中用于自动装配和初始化各种组件、功能和设置。

这个META-INF/spring.factories文件指什么呢?

在我们导入的每一个XXX-spring-boot-starter中,除了本身的jar包以外,还会有一个 xxx-spring-boot-autoConfigure,这个就是第三方依赖自己编写的自动配置类。我们现在就以 spring-boot-autocongigure 这个依赖来说。

【SpringBoot】SpringBoot的自动配置源码解析,SpringBoot,spring boot,java,spring

这些类就是自动配置的类了。就RedisAutoConfiguration而言

@AutoConfiguration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

里面已经配置好了,Redis参数指定了RedisProperties配置文件,还有数据源配置文件。

我们只需要在yml文件配置好,后面的全交给SpringBoot框架自己配置即可。

【SpringBoot】SpringBoot的自动配置源码解析,SpringBoot,spring boot,java,spring


4. 常用的Conditional注解

@Conditional其实是spring底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效。

  1. @ConditionalOnClass : classpath中存在该类时起效
  2. @ConditionalOnMissingClass : classpath中不存在该类时起效
  3. @ConditionalOnBean : DI容器中存在该类型Bean时起效
  4. @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
  5. @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
  6. @ConditionalOnExpression : SpEL表达式结果为true时
  7. @ConditionalOnProperty: 参数设置或者值一致时起效
  8. @ConditionalOnResource : 指定的文件存在时起效
  9. @ConditionalOnJndi: 指定的JNDI存在时起效
  10. @ConditionalOnJava: 指定的Java版本存在时起效
  11. @ConditionalOnWebApplication : Web应用环境下起效
  12. @ConditionalOnNotWebApplication : 非Web应用环境下起效

参考:文章来源地址https://www.toymoban.com/news/detail-559559.html

  • 一文搞懂🔥SpringBoot自动配置原理 - 掘金 (juejin.cn)
  • SpringBoot自动配置原理详解 - 掘金 (juejin.cn)

到了这里,关于【SpringBoot】SpringBoot的自动配置源码解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【SpringBoot】SpringBoot的自动配置源码解析

    【SpringBoot】SpringBoot的自动配置源码解析

    SpringBoot相对于SSM来说,主要的优点就是简化了配置,不再需要像SSM哪有写一堆的XML配置,这些XML配置在大项目上会成为一种累赘,使得后期项目难以维护。 SpringBoot的出现,使得开发者不再关注于配置,能够更加专注于业务的开发,这得益于SpringBoot的自动配置。 SpringBoot的自

    2024年02月16日
    浏览(5)
  • 拆解Spring boot:Springboot为什么如此丝滑而简单?源码剖析解读自动装配

    拆解Spring boot:Springboot为什么如此丝滑而简单?源码剖析解读自动装配

    🎉🎉欢迎光临,终于等到你啦🎉🎉 🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.csdn

    2024年03月23日
    浏览(10)
  • Java 框架面试题-Spring Boot自定义配置与自动配置共存

    Java 框架面试题-Spring Boot自定义配置与自动配置共存

    Spring Boot 是一个快速开发框架,可以简化 Spring 应用程序的开发,其中自定义配置是其中一个非常重要的特性。 在 Spring Boot 中,自定义配置允许开发者以自己的方式来配置应用程序。自定义配置可以用于覆盖默认配置,也可以用于添加新的配置项。本文将详细介绍 java框架面

    2023年04月11日
    浏览(9)
  • Spring Boot——Spring Boot自动配置原理

    Spring Boot——Spring Boot自动配置原理

    Spring Boot启动原理 一直在使用Spring Boot特别好奇的是为什么Spring Boot比Spring在项目构建和开发过程中要方便很多,无需编写大量的配置,Spring Boot自动给你配置好了。往往是集成项目依赖之后一键使用。于是小编我就学习和研究了一下Spring Boot的自动配置。 主程序入口示例 :

    2024年02月15日
    浏览(6)
  • Spring Boot 属性配置解析

    Spring Boot 属性配置解析

    基于Spring Boot 3.1.0 系列文章 Spring Boot 源码阅读初始化环境搭建 Spring Boot 框架整体启动流程详解 Spring Boot 系统初始化器详解 Spring Boot 监听器详解 Spring Boot banner详解 Spring Boot 3.1.0 支持的属性配置方式与2.x版本没有什么变动,按照以下的顺序处理,后面的配置将覆盖前面的配置

    2024年02月09日
    浏览(11)
  • 【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理

    【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理

    💓 博客主页:从零开始的-CodeNinja之路 ⏩ 收录文章:【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理 🎉欢迎大家点赞👍评论📝收藏⭐文章 配置文件主要是为了解决硬编码带来的问题,把可能会发生改变的信息,放在⼀个集中的地方,当我们启 动某个程序时,应用程

    2024年04月23日
    浏览(10)
  • spring boot 自动配置

    spring boot 自动配置

    自动配置介绍 Spring Boot自动装配(Auto Configuration)是Spring Boot框架的一个关键特性,它通过约定大于配置的方式来简化项目的配置过程。自动装配允许开发人员使用默认的配置,同时也可以根据需要进行定制化。Spring通过使用 @Autowired 注解、 @ComponentScan 注解以及条件化配置等

    2024年01月25日
    浏览(11)
  • Spring Boot自动配置原理

    Spring Boot自动配置原理

    springboot是基于spring的新型的轻量级框架,最厉害的地方当属**自动配置。**那我们就可以根据启动流程和相关原理来看看,如何实现传奇的自动配置 从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以要揭开SpringBoot的神秘面纱

    2024年01月19日
    浏览(10)
  • Spring Boot 自动配置原理

    一、概述 相比较于Spring 繁杂的xml配置,Spring Boot 的自动配置极大的简化了开发 二、自动配置过程 1、引入场景启动器starter 2、SPI思想,自动扫描和加载META-INF下面的配置类 3、 配置类 通过@Bean配置组件 4、配置类通过@EnableConfigurationProperties将配置类与属性类绑定 5、 属性类

    2024年03月14日
    浏览(13)
  • 聊聊Spring Boot的自动配置

    聊聊Spring Boot的自动配置

    介绍 Spring Boot的自动配置是其一大核心特性,可以让我们迅速构建出一个Spring应用程序。 Spring Boot 是 Spring Framework 中最火的项目之一,它通过集成各种第三方库和框架来简化开发过程,并提供一些优秀的默认设置,使得开发人员可以快速构建高效的企业级应用。本文将主要讲

    2024年02月10日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包