SpringBoot中自动配置的原理(启动类注解)
创始人
2025-05-31 06:08:39
0

Java知识点总结:想看的可以从这里进入

目录

      • 1.4、自动配置和主启动类
        • 1.4.1、启动类注解
        • 1.5.2、导入选择器
        • 1.5.3、自动配置机制
          • 1、factories 文件
          • 2、实现原理
        • 1.5.4、自动配置的图解

1.4、自动配置和主启动类

什么是自动配置?SSM在使用时需要进行一系列的配置,比如配置 DispatcherServlet、配置视图解析器、配置事务管理器等等、配置各个包的扫描,它的很多组件需要自己配置后才可以使用。而SpringBoot在没有任何配置的情况下,仅仅导入相应的启动器后,即可运行项目,访问Controller、视图跳转等等,这都要归功于其自动配置。

1.4.1、启动类注解

SprinBoot有内置的Servlet,所以它不要配置服务器,直接通过一个启动类来运行项目,而这个启动类就是SpringBoot能实现自动配置的核心所在。

我们在观察启动类时,会发现在启动类上有一个注解@SpringBootApplication,这个就是springboot的核心注解,很多原理都在这个注解里面。

@SpringBootApplication
public class SpringbootApplication {public static void main(String[] args) {//启动SpringBoot的程序SpringApplication.run(SpringbootApplication.class, args);}
}

@SpringBootApplication是SpringBoot的启动类的注解,表示该类是一个启动类。它是一个组合注解,内部包含了多个注解,我们可以直接点击此注解看内部有什么注解:

image-20220919172132802

它内部的注解还是挺多的,但是其中有三个核心的注解:@SpringBootConfiguration、@ComponentScan 、@EnableAutoConfiguration,一个底层是spring的核心的配置类,第二个用来扫描包,最后一个是 SpringBoot 可以自动配置的原因所在。所以说SpringBoot是在spring的基础上延伸的。

  • @SpringBootConfiguration:将类标记为SpringBoot的配置类,是Spring 中 代替XML配置的注解的相关延伸。

    image-20230307145438639
    • @Configuration:spring的核心配置类
      • @Component:spring一个组件
  • @ComponentScan:用于包的扫描,默认是启动类所在的包及其子包,主要是扫描@Component、@Service、@Controler、@Repository、@Configuration等等注解,并将其所在类注册为 bean对象。

  • @EnableAutoConfiguration:是SpringBoot能自动化配置的核心所在,用于开启 Spring Boot 的自动配置功能,进入后其内部也有两个重要的注解:

    image-20220919174109400
    • @AutoConfigurationPackage:用来自动配置包

      image-20220919174044796
      • @Import(AutoConfigurationPackages.Registrar.class):导入自动配置包的选择器

        • AutoConfigurationPackages类的内部类Registrar:首先获取到注解所标识的类,然后将这个类所在的包以及子包,并将其名字放入到一个String数组当中,再将该String数组中的包的所有组件导入到Spring的 容器当中(自动导入主程序所在包及其子包的所有组件)

          static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//获取注解元信息,得到所有包名,封装到数组里面,然后批量注册register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}
             
        • @Import(AutoConfigurationImportSelector.class):自动配置导入选择器(牵扯到自动配置机制,spring-boot-autoconfigure-x.x.x.jar包中 META-INF/spring.factories文件)

          • AutoConfigurationImportSelector类:帮助SpringBoot将符合条件的@Configuration配置都加载到IOC容器中
            • getImportGroup() 方法
            • process() 方法
            • selectImports() 方法
        • 1.5.2、导入选择器

          AutoConfigurationImportSelector 自动配置导入选择器,是实现自动配置的核心类,他会自动会给容器中导入非常多的配置类(xxxAutoConfifiguration),它实现自动配置的步骤如下:

          • selectImports()方法:核心方法,它将 注解元数据 传递给 getAutoConfigurationEntry() 方法,获得自动配置的所有条目

            @Override
            public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}//通过getAutoConfigurationEntry 方法获取需要加载的全部类名AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
            
          • getAutoConfigurationEntry()方法: 通过调用 getCandidateConfigurations() 方法来获取自动配置类的完全限定名,并在经过排除、过滤等处理后,将其缓存到成员变量中,

            //根据导入的@Configuration类的AnnotationMetadata返回AutoConfigurationImportSelector.AutoConfigurationEntry 
            protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}//获取注解元数据中的属性设置AnnotationAttributes attributes = getAttributes(annotationMetadata);//获取需要自动配置的类List configurations = getCandidateConfigurations(annotationMetadata, attributes);//删除重复的配置类configurations = removeDuplicates(configurations);//获取导入的配置类Set exclusions = getExclusions(annotationMetadata, attributes);//检查是否还存在排除配置类checkExcludedClasses(configurations, exclusions);//删除排除的配置类configurations.removeAll(exclusions);//获取过滤器,过滤配置类configurations = getConfigurationClassFilter().filter(configurations);//触发自动配置导入事件fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
            }
            
          • getCandidateConfigurations()方法:此方法负责获取所有的自动配置类的名称。

            根据 Spring Factories 机制调用 SpringFactoriesLoader 的 loadFactoryNames() 方法,根据 EnableAutoConfiguration.class (自动配置接口)获取其实现类(自动配置类)的类名的集合

            protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List configurations = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);//在 META-INFspring.factories 和 META-INFspringorg.springframework.boot.autoconfigure.AutoConfiguration.imports 中都找不到自动配置类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;
            }
            

            image-20230308111415626

          1.5.3、自动配置机制

          Spring Boot 的自动配置是基于 Spring Factories 机制实现的,它是一种服务发现机制,这种扩展机制与 Java SPI 机制十分相似。Spring Boot 会自动扫描所有 Jar 包类路径下 META-INF/spring.factories 文件,并读取其中的内容,进行实例化,这种机制也是 Spring Boot Starter 的基础。

          1、factories 文件

          Spring Boot 的很多 Jar包内都有 META-INF/spring.factories 这个文件,比如:

          image-20230307151035083

          spring.factories和 .properties文件格式类似通过键值的格式(key=value),key为接口的全限定名,value为接口实现类的全限定名。

          image-20230307151105294
          2、实现原理

          在 spring-core的包内有 SpringFactoriesLoader 类,它会扫描 META-INF/spring.factories 文件,并获取指定的接口配置

          image-20230307160149806
          • loadFactoryNames方法:AutoConfigurationImportSelector 中的 getCandidateConfigurations()方法就是调用此方法来获取配置类,它用来从"META-INF/spring.factories" 获取配置类的全限定名。

            public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();//调用loadSpringFactories,获得一个Map> 集合,然后从这个集合中拿出 configurationsreturn (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
            }
            
          • loadSpringFactories():方法能够读取该项目中所有 Jar 包类路径下 META-INF/spring.factories 文件的配置内容,并以 Map 集合的形式返回 到loadFactoryNames 方法

            Map,值为一个list集合,一个注解的类名为键——多个Config配置类为值的形式

            private static Map> loadSpringFactories(ClassLoader classLoader) {//从缓存那种获取。如果缓存中存在就直接返回,不存在再去相应的路径去加载Map> result = cache.get(classLoader);if (result != null) {return result;}result = new HashMap<>();try {//扫描所有 Jar 包类路径下的 META-INF/spring.factories 文件Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);while(urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);//将扫描到的 META-INF/spring.factories 文件中内容包装成 properties 对象Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry entry : properties.entrySet()) {//提取 properties 对象中的 key 值String factoryTypeName = ((String) entry.getKey()).trim();//提取 proper 对象中的 value 值(多个类的完全限定名使用逗号连接的字符串)// 使用逗号为分隔符转换为数组,数组内每个元素都是配置类的完全限定名String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());//遍历配置类数组,并将数组转换为 list 集合for (String factoryImplementationName : factoryImplementationNames) {result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}//将 propertise 对象的 key 与由配置类组成的 List 集合一一对应存入名为 result 的 Map 中result.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), 	                     Collections::unmodifiableList)));//这里将查询出来的都加入到缓存中这样下次就可以从缓存中获取了cache.put(classLoader, result);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;
            }
            
          • loadFactories方法:使用给定的类加载器从"META-INF/spring.factories"加载并实例化给定类型的工厂实现

            public static  List loadFactories(Class factoryType, @Nullable ClassLoader classLoader) {Assert.notNull(factoryType, "'factoryType' must not be null");ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}// 调用loadFactoryNames获取接口的实现类List factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);}List result = new ArrayList<>(factoryImplementationNames.size());for (String factoryImplementationName : factoryImplementationNames) {result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result;
            }
            

          1.5.4、自动配置的图解

          springboot自动配置的图解

          这些注解可以决定配置文件在什么情况下生效:

          image-20210824164512110

          相关内容

          热门资讯

          小波包分解提取 %% wavelet packet coefficients. 求取小波包分解的各个节点的小波包系数...
          算法小抄9-快慢指针 在上一节我们已经讲过了怎么使用快慢指针去寻找链表的中点,这一节我们继续学习快慢指针的进阶用法,快慢指...
          机械学院职业生涯规划书 机械工...  机械学院职业规划书(一)  第一章 认识自我  尼采曾说:“聪明的人只要能认识自己,便什么都不会失...
          化学系职业规划书范文 化学系大...   化学系职业规划书(一)  一。 前言  社会的发展日异月新,社会的竞争越来越大,大学生越来越多,...
          土木工程学院职业生涯规划书 土...   土木工程学院职业生涯规划书(一)  一、前言  莎士比亚曾说过:“人生就是一部作品。谁有生活理想...
          利用python写一个gui小... 文章目录前言制作设计自己想要的ui举个例子后记 前言 在上文中我们配置好了GUI编译环境࿰...
          体育学院职业规划书 体育学院职...  体育学院职业规划书(一)  我们向往着美好的明天,所以一直为着心中的那个梦而奋斗。大学生职业生涯规...
          护理专业职业生涯规划书 我的职...   护理专业职业生涯规划书(一)  一、前言:  开学了,又一批大一的新生步入了象牙塔,在高三饱受折...
          【设计模式】23种设计模式之创... 【设计模式】23种设计模式之创建型模式一、单例模式1、是什么?2、单例模式有哪八种方式...
          外语学院职业生涯规划书 外语大...   外语学院职业生涯规划书(一)  在已经步入二十一世纪的今天,大学生已经与时代接轨赋予了全新的含义...
          基于点云的深度学习方法预测蛋白... 论文题目: 研究背景: 1. 三维空间结构及相关信息可以通过深度图像&...
          背叛的签名 与背叛有关的签名 ... 人的一生,最不能忍的就是被自己最亲的人背叛,在签名里,有哪些签名是与背叛有关的呢?下面请欣赏太阳教育...
          离别的签名 有关离别的签名 离... 离别的场景,每个人都不想看到,那么,在签名里,有哪些文字是与离别有关的呢?下面请欣赏太阳教育网小编为...
          最新或2023(历届)最新分别... 当我们与别人分离的时候,心情会是怎样的呢?你是否需要为自己的签名换上与分别有关的句子呢?下面请欣赏太...
          表达与别人分离的个性签名 分开... 当和别人分离的时候,你会用怎样的一句个性签名来表达自己的心情呢?下面请欣赏太阳教育网小编为大家推荐的...
          分别的签名 与分别有关的签名 ... 人生总是在兜兜转转,有聚便会有散,在签名里,有哪些句子是与分别有关的呢?下面请欣赏太阳教育网小编为大...
          个性签名骂朋友的话语 个性签名...  阁下长得真是天生励志!比较有个性一点儿的骂朋友的话语还有哪些呢?下面是太阳教育网小编为你搜集的个性...
          个性签名的话 QQ个性签名的话... 得不到的,会一直被渴望着,而失去的,也总是被铭记的。适合用来做qq个性签名的话语还有哪些呢?下面是太...
          最新或2023(历届)最新唯美... 喜欢唯美古风的你,知道在最新或2023(历届)出现了哪些与唯美古风有关的签名吗?下面请欣赏太阳教育网...
          兄弟分别的签名 表达兄弟分别的... 兄弟是我们在这一生中难得的朋友,知己,当我们与兄弟分离的时候,如何用一句签名来表达我们当时的心情?下...