代码编织梦想

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

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 {
          
          		@Override
          		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
          			//获取注解元信息,得到所有包名,封装到数组里面,然后批量注册
                      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
          		}
          
          		@Override
          		public Set<Object> 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<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    	//删除重复的配置类
        configurations = removeDuplicates(configurations);
        //获取导入的配置类
        Set<String> 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<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> 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<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        String factoryTypeName = factoryType.getName();
        //调用loadSpringFactories,获得一个Map<String, List<String>> 集合,然后从这个集合中拿出 configurations
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
  • loadSpringFactories():方法能够读取该项目中所有 Jar 包类路径下 META-INF/spring.factories 文件的配置内容,并以 Map 集合的形式返回 到loadFactoryNames 方法

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

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        //从缓存那种获取。如果缓存中存在就直接返回,不存在再去相应的路径去加载
        Map<String, List<String>> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        result = new HashMap<>();
        try {
            //扫描所有 Jar 包类路径下的 META-INF/spring.factories 文件
            Enumeration<URL> 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 <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryType, "'factoryType' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        // 调用loadFactoryNames获取接口的实现类
        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
        }
        List<T> 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
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yuandfeng/article/details/129661370

面试题: SpringBoot 的自动配置原理-爱代码爱编程

个人博客网:https://wushaopei.github.io/    (你想要这里多有) 3、Spring Boot 的自动配置原理 package com.mmall; import org.springframework.boot.SpringApplication; import org.springframework.boot.auto

SpringBoot源码深度解析(一):SpringBoot的自动配置原理-爱代码爱编程

前言 SpringBoot流行之前,程序员大多是用SSM框架整合来进行WEB后端开发。这种方式非常麻烦,需要手动引入大量的包,还要配置很多XML文件,光是搭建环境就需要很久。 随着“约定大于配置”理念的流行,SpringBoot随之兴起,它大大简化了web开发的流程,可以让初学者快速上手。SpringBoot的核心理念大致有3点: 1、帮助开发者快速

SpringBoot的自动配置原理-爱代码爱编程

SpringBoot是一个约定大于配置的典范,在启动SpringBoot时,只有一个注解SpringBootApplication点进去这个注解,里面有三个比较重要的注解 1.其中SpringBootConfiguration的底层是Configuration注解,说明SpringBoot使用配置类来代替XML文件。 2.ComponentScan将C

SpringBoot:自动配置原理(一文搞懂)-爱代码爱编程

自动装配原理 pom.xml 依赖管理 <!--有一个父项目,用来做依赖管理的--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-paren

springboot的自动配置原理和启动原理_后端咸鱼的博客-爱代码爱编程

1.自动配置的原理 springBoot项目都会有一个启动类,启动类上有一个@SpringBootApplicaion注解,这个注解是一个组合注解,主要有三部分组成: 1、@SpringBootConfiguration 2、@EnableAutoConfiguration 3、@CompoentScan 其中,第一个注解表明了这个启动类是一个配置类

springboot的自动配置原理_星星慢的博客-爱代码爱编程

什么是Springboot的自动配置 Springboot:是对Spring框架做的一系列优化,减少了大量繁琐的配置,并提供了内置web服务器,让程序运行更快。 自动配置:用注解来对一些常规的配置做默认配置,简化xml配置内容,使你的项目能够快速运行。自动装配最明显的特征就是不用xml文件去做配置,而是用properties文件或者yml文件,简单地写

springboot的核心注解及自动配置原理_never debug的博客-爱代码爱编程

文章目录 1. 前言2. SpringBoot的核心注解3. SpringBoot的自动配置3.1 @AutoConfigurationPackage注解3.2 @Import注解 4. 按需开启自动配置4.1

深入springboot启动流程+自动配置原理-爱代码爱编程

深入Springboot启动流程+自动配置原理 🍅写在前面🍅相关常见面试题 1.Springboot启动类1.1.@SpringBootConfiguration解读1.2.@ComponentScan解

springboot接口 -爱代码爱编程

上文我们看到可以通过Swagger系列可以快速生成API文档, 但是这种API文档生成是需要在接口上添加注解等,这表明这是一种侵入式方式; 那么有没有非侵入式方式呢, 比如通过注释生成文档? 本文主要介绍非侵入式的方式及集成Smart-doc案例。我们构建知识体系时使用Smart-doc这类工具并不是目标,而是 要了解非侵入方式能做到什

java认证授权(spring security)_java security-爱代码爱编程

目录 1、Spring Security介绍 2、认证授权入门 2.2.1 创建认证服务工程 1、部署认证服务工程 2、配置Spring Security所需要的依赖 3、初始工程自带了一个Controller类,如下 4、需要进行安全配置。 3、授权测试 1、配置用户拥有哪些权限。 2、指定资源与权限的关系。 4、工作原理 Spr

java版工程管理系统spring cloud+spring boot+mybatis实现工程管理系统_java 星工程管理系统-爱代码爱编程

     java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统 工程项目各模块及其功能点清单 一、系统管理     1、数据字典:实现对数据字典标签的增删改查操作     2、编码管理:实现对系统编码的增删改查操作     3、用户管理:管理和查看用户角色     4、菜单管理:实现对系统菜单的增删改查操