一、什么是SpringBoot自动配置

SpringBoot自动配置,英文是 Auto-Configuration:

  • 它是指基于你引入的依赖Jar包,对SpringBoot应用进行自动配置
  • 它为SpringBoot框架的 开箱即用提供了支撑

注意事项:

自动配置: Auto-Configuration

自动装配: Autowire

二、术语“配置类” Configuration Class

广义的配置类:

  • 被注解@Component直接或间接修饰的某个类
  • 即我们常说的Spring组件,其中包括了@Configuration类

狭义的配置类:

  • 特指被注解@Configuration所修饰的某个类
  • 又被称为@Configuration类

1、常见的配置类形式

1-常见的配置类.png

2、Redis使用演示

  • ①引入Starter起步依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • ②配置Redis相关信息
spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password: 123456
  • ③可以直接使用Redis相关对象
@Autowired
private RedisTemplate redisTemplate;

@Autowired
private StringRedisTemplate stringRedisTemplate;	

三、SpringBoot启动流程简化版

public static void run(Class<?> primaryClass){
    // 1. 创建一个ApplicationContext实例,即我们常说的IoC容器
    ApplicationContext context createApplicationContext();
    
    // 2. 将主类(primaryclass)注册到IoC容器中(简单但重要的第一步)
    loadSourceclass(context,primaryClass);
    
    // 3. 递归加载并处理所有的配置类
    processConfigurationClasses(context);
    
    // 4. 实例化所有的单例Bean(Singleton Bean)
    instantiateSingletonBeans(context);
    
    // 5. 如果是web应用,则启动web服务器(例Tomcat)
    startWebServer(context);
}
  • 流程图

2-SpringBoot启动流程简化版.png

四、SpringBoot加载配置类的流程简化版

public void parse(Class<?> configClass){
    // 1. 处理@ComponentScan: 根据@ComponentScan扫描指定的package,得到一系列配置类
    if (hasComponentScan(configClass)){
    	for (Class<?> clazz : doScan(configClass))
    		this.parse(clazz);//递归处理
    }
    
    // 2. 处理注解@Import: 根据注解@Import,得到一系列被导入的配置类
    if (hasImportedClasses(configClass)){
    	for (Class<?> clazz : getImports(configClass))
    		this.parse(clazz);//递归处理
        
    // 3. 处理@Bean方法
    processBeanMethods(configClass);
        
    // 4. 处理@Import导入ImportBeanDefinitionRegistrar
    processRegistrars(configclass);
        
    // 5. 加入到一个全局的配置类集合中
    this.configurationClasses.add(configclass);
}
  • 流程图

3-加载配置类.png

1、@ComponentScan

他是一个来自Spring框架的一个注解:

  • 它的作用是对指定的package进行扫描,找到其中符合条件的类,默认是搜索被注解@Component修饰的配置类
  • 通过属性basePackages或basePackageClasses,来制定要扫描的包或类
  • 如果未指定package,则默认扫描当前@ComponentScan所修饰的类所在的package

示例:

4-ComponentScan示例.png

2、@Import

它是来自Spring框架的一个注解:

  • 它的作用是提供了一种显示地从其他地方加载配置类的方式,这样可以避免使用性能较差的组件扫描(@ComponentScan)
  • 支持导入:
    • 普通类(这里的“普通”,是相对于后面的两个接口而言的)
    • 接口ImportSelector选择器的实现类
    • 接口ImportBeanDefinitionRegistrar注册器的实现类

2.1 导入普通类

可以通过@Import导入一个普通类,效果类似于这个普通类被注解@Component所修饰

  • 创建两个类
// 第一个类
public class ConfigA{
    //注入A的实例 
    @Bean
    public A a(){
        return new A();
    }
}

// 第二个类
public class A{
    
}
  • 创建一个配置类,直接将刚才创建的ConfigA导入
@Configuration
@Import(ConfigA.class)
public class ConfigB(){
    
}
  • 最后ConfigA和A对象都注入到了IOC容器中

2.2 导入ImportSelector选择器(自动配置的实现)

​ 可以通过@Import导入一个接口ImportSelector实现类。

​ 接口ImportSelector中有一个selectlmports方法,它的返回值是一个字符串数组,数组中的每个元素分别代表一个将被导入的配置类的全限定名。

利用该特效,我们可以给IOC容器动态地导入多个配置类

  • 创建两个类
// 第一个类
public class ZooConfig(){
    //注入Tiger的实例
    @Bean
    public Tiger tiger(){
        return new Tiger();
    }
}

//第二个类
public class Tiger{
    
}
  • 创建ImportSelector的实现类
public class ZooImportSelector implements ImportSelector{
    @Override
    public String[] selectImports(AnnotationMetadata metadata){
        return new String[]{"com.leaflei.ZooConfig"}
    }
}
  • 创建一个配置类,将ImportSelector实现类导入
@Configuration
@Import({ZooImportSelector.class})
public class ConfigB{
    
}
  • 最后ZooConfig和Tiger对象都注入到了IOC容器中

2.3 导入ImportBeanDefinitionRegistrar实现类

​ 可以通过@Import导入一个接口ImportBeanDefinitionRegistrar的实现类。通过它,我们可以手动将多个BeanDefinition注册到loC容器中,从而实现个性化的定制。

利用该特性我们可以给IOC容器动态地导入多个BeanDefinition

  • 创建一个普通类
public class Dog{

}
  • 创建一个ImportBeanDefinitionRegistrar的实现类
public class ZooRegistrar implements ImportBeanDefinitionRegistrar{
    @Override
    public void registerBeanDefinitions(
    	AnnotationMetadata importingClassMetadata,BeanDefinitionRegistrar registry){
        GenericBeanDefinition bd =new GenericBeanDefinition();
        // 手动注册一个类型为Dog的对象
        bd.setBeanClass(Dog.class);
        registry.registerBeanDefinition("dog",bd);
    }
}
  • 创建一个配置类,将ZooRegistrar类导入
@Configuration
@Import({ZooRegistrar.class})
public class ConfigB{

}
  • 最后dog对象已经成功注入到IOC容器中

五、SpringBoot自动配置原理图

5-自动配置的原理.png

1、如何实现AutoConfigurationImportSelector

  • SpringFactories机制
    • Java SPI机制的延伸和扩展
    • Spring框架的基础机制,在Spring以及SpringBoot源码中到处可见
    • 可以基于它来实现SpringBoot自动配置功能

2、SpringFactories机制

6-JavaSPI机制与SpringFactories机制对比.png

3、源码剖析

  • AutoConfigurationImportSelector关键源码

7-AutoConfigurationImportSelector关键源码.png

  • getAutoConfigurationEntry自动配置入口

8-getAutoConfigurationEntry自动配置入口.png

  • getCandidateConfigurations获取第三方jar包的自动配置类

9-getCandidateConfigurations获取第三方jar包的自动配置类.png

4、总结实现流程

  • 总结AutoConfigurationImportSelector的实现流程

10-总结AutoConfigurationImportSelector的实现流程.png

5、@Conditional

它是来自Spring框架的一个注解:

  • 它的作用是实现: 只有在特定的条件满足时,才回向IOC容器中注册指定的组件
  • 我们可以将它理解为某种IF语句

5.1 扩展注解

11-Conditional扩展注解.png

6、流程总结

12-总结流程.png

六、Redis为例解读自动配置

  • Redis的依赖

13-Redis起步依赖.png

  • 查看Reids的项目下的spring.factories配置文件

14-redis的spring.factories.png

  • 查看Reids的RedisAutoConfiguration自动配置类

15-查看RedisAutoConfiguration自动配置类.png