源码角度解析SpringBoot 自动配置

文章目录

  • 前言
  • 一、了解相关注解
    • 1.@Condition注解
    • 2.@Enable注解
  • 二、SpringBoot自动配置
    • 1.@SpringBootApplication注解
    • 2.@SpringBootConfiguration注解
    • 3.@EnableAutoConfiguration注解
    • 4.@Conditional注解
  • 总结


前言

Spring Boot 自动配置是 Spring Boot 的核心特性之一,它的目标是通过分析应用程序的类路径(classpath)和依赖关系,来自动配置 Spring 应用程序所需的 Bean、设置和组件。这意味着开发人员不需要手动配置大部分常见的 Spring 组件,Spring Boot 将根据环境和依赖来进行智能配置。这样,开发人员可以专注于编写业务逻辑,而不必过多关心底层配置细节。


一、了解相关注解

1.@Condition注解

Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。
SpringBoot 提供的常用条件注解:

  • @ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
  • @ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
  • @ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
  • @ConditionalOnBean:判断环境中有对应Bean才初始化Bean

2.@Enable注解

SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。
@Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:​
① 导入Bean;
@Import(User.class)
② 导入配置类;
@Import(UserConfig.class)
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类;
@Import(MyImportSelector.class)
④ 导入 ImportBeanDefinitionRegistrar 实现类。
@Import({MyImportBeanDefinitionRegistrar.class})

二、SpringBoot自动配置

1.@SpringBootApplication注解

主启动类注解:

package org.springframework.boot.autoconfigure;
@Targer(ElementType.TYPE)//作用于类
@Retention(Retention.RUNTIME)//该注解在运行时可见,可以通过反射读取
@Documented//文档相关
@Inherited//可以被子类继承
@SpringBootConfiguration//标记该类为 Spring Boot 的配置类
@EnableAutoConfiguration//实现自动配置功能
@ComponentScan(excludeFilters = {
        @Filter(type=FilterType.CUSTOM,classes = TypeExcludeFilter.class),
        @Filter(type=FilterType.CUSTOM,classes = AutoConfigurationExcludeFilter.class)
})//配置组件扫描,以查找和注册 Spring Bean
public @interface SpringBootApplication{……}

总的来说,大概可以把@SpringBootApplication看作是@Configuration、@EnableAutoConfigeration、@ComponentScan注解的集合。
@EnableAutoConfigeration:启用SpringBoot的自动配置机制;
@ComponentScan:扫描被@Component(@Service,@Controller)注解的bean,注解默认会扫描该类所在包下的所有的类;
@Configuration:允许在上下文中注册额外的bean或倒入其他配置类。

2.@SpringBootConfiguration注解

SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

//这里的@Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
@Configuration
public @interface SpringBootConfiguration {}
//里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
@Component
public @interface Configuration {....}

3.@EnableAutoConfiguration注解

@EnableAutoConfiguration开启自动配置功能,它可以说是我们研究SpringBoot 自动配置的重中之重了:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {.....}

3.1@AutoConfigurationPackage注解:

//AutoConfigurationPackage的子注解
//Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

在默认的情况下就是将:主配置类(@SpringBootApplication)的所在包及其子包里边的组件扫描到Spring容器中。

3.2@Import({AutoConfigurationImportSelector.class})注解:给容器导入组件
AutoConfigurationImportSelector :自动配置导入选择器,给容器中导入一些组件;
通过了@import注解导入了AutoConfigurationImportSelector这个配置类,根据类名可以发现,这个实现类是@Import提供4中用法中的ImportSelector的实现类,那么它就一定实现了selectImports这个个方法

String[] selectImports(AnnotationMetadata importingClassMetadata);

该方法的主要目的是根据运行时的条件或配置动态地确定要导入哪些配置类,以扩展或自定义 Spring 应用程序的配置。
返回值是一个字符串数组,其中包含需要导入到当前配置类中的其他配置类的全限定类名。
那我们就来看看他是怎么来重写这个方法的:

 public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

this.isEnabled(annotationMetadata) 方法检查是否启用了自动配置。如果没有启用,就返回一个空数组 NO_IMPORTS,表示不导入任何配置类。
getAutoConfigurationEntry(annotationMetadata) 方法来获取自动配置入口,那么他是如何去获取的呢?继续进方法看看:

List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

用于根据给定的注解元数据 (annotationMetadata) 和属性 (attributes) 获取潜在的自动配置类的列表,它的作用就是找到所有可能与应用程序上下文相关的自动配置类。
↓ ↓ ↓

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), 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;
    }

这个方法想告诉我们什么呢,其实看到这里官方给出的提示就已经很明显了:

在META-INF/spring中找不到自动配置类。在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中。如果您正在使用自定义打包,请确保该文件是正确的。

简但来说,就是在所有包名叫做autoConfiguration的包下面都有META-INF/spring.factories文件,Spring启动的时候会扫描这个文件,将其文件包装成Properties对象从Properties对象获取到key值为EnableAutoConfiguration的数据,然后添加到容器里边。

源码角度解析SpringBoot 自动配置_第1张图片

4.@Conditional注解

现在SpringBoot自动配置信息有了,自动配置还差什么呢?
到这一步为止,SpringBoot已经为我们的项目中导入了大量的自动配置类,且项目中的自动配置类全部执行,但是这133个自动配置类我们都会用到吗?
以RedisAutoConfiguration为例:
源码角度解析SpringBoot 自动配置_第2张图片
看到这里就不难发现,虽然SpringBoot帮我们加载了Redis的自动配置类,但并没有把redisTemplate模板类帮我们注入容器,真正想要注入模板类到容器里供我们使用之前,还要经过一些选择注解的判断且全部成功才可以成功注入。
以redisTemplate模板类为例,它想要成功注入,就得经过三个选择注解的全部成功才可以注入:

//项目中必须要有RedisProperties这个类才可以生效(坐标中倒入)
@EnableConfigurationProperties({RedisProperties.class})
//容器中必须没有一个Bean为redisTemplate,这也就允许我们自己向容器中注入一个redisTemplate!
@ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
 //指定必须是单例工厂才能生效
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)

只有在经过这三个选择注解后,自动装配才算真正完成,redisTemplate类才能供我们在项目中使用。


总结

  • @EnableAutoConfiguration 注解内部使用@Import(AutoConfigurationImportSelector.class)来加载配置类;
  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot应用启动时,会自动加载这些配置类,初始化Bean;
  • 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean。

你可能感兴趣的:(spring,boot,java,spring,SpringBoot自动配置)