自动配置原理

Auto-Configuration

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

术语“配置类”,英文Configuration Class

  • 广义的“配置类”:被注解@Component直接或间接修饰的某个类,即我们常说的Spring组件,其中包括了@Configuration
  • 狭义的“配置类”:特指被注解@Configuration所修饰的某个类,又称为@Configuration

相关注解

@ComponentScan

@ComponentScan,是来自Spring框架的一个注解:

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

@Import

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

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

@SpringBootApplication

  • @SpringBootConfiguration
    • @Configuration
  • @EnableAutoConfiguration
    • @Import(AutoConfigurationImportSelector.class)
  • @ComponentScan

@SpringBootApplication修饰的类,也会被@Configuration简介修饰,即“原配置类”

SpringBoot框架会对“原配置类”所在的package进行组件扫描(ComponentScan

SpringBoot框架最终会导入AutoConfigurationImportSelector来实现自动配置

@Conditional

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

常用:

注解名称 作用
ConditionalOnBean 当容器存在指定的Bean时,满足条件
ConditionalOnMissingBean 当容器不存在指定的Bean时,满足条件
ConditionalOnClass 当classpath中存在指定的类时,满足条件
ConditionalOnMissingClass 当classpath中不存在指定的类时,满足条件

自动配置类实例

Redis的自动配置

  1. 引入依赖
  2. 配置Redis服务器
  3. 直接使用RedisTemplateStringRedisTemplate
1
2
3
4
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;

SpringBoot的启动流程(简化版)

简化版代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void run(Class<?> primaryClass){
//1.创建一个ApplicationContext实例,及我们常说的IOC容器
ApplicationContext context=createApplicationContext();
//2.将主类或者称为原配置类(primaryClass)注册到IoC容器中(简单但最重要的第一步)
//原配置类:通常是main方法所在的类,而且会被注解@SpringBootApplication所修饰,我们又称之为主类
loadSourceClass(context,primaryClass);
//3.递归加载并处理所有的配置类
//SpringBoot会自动找到所有的配置类,然后加载和处理它们
processConfigurationClasses(context);
//4.实例化所有的单例Bean(Singleton Bean)
//实例化所有的单例Bean。“依赖注入”和“自动装配”就属于其中的环节
instantiateSingletonBeans(context);
//5.如果是web应用,则启动web服务器(例如Tomcat)
startWebServer(context);
}

加载并处理所有的配置类processConfigurationClasses()

1
2
3
4
5
6
7
8
9
10
11
public static void processConfigurationClasses(ApplicationContext context){
//1.首先从IoC容器中取出当前存在的原配置类
Class<?>sourceConfigurationClass=getSourceConfigurationClass(context);
//2.创建一个配置类解析器,然后递归加载并处理应用中所有的配置类
ConfigClassParser parser=new ConfigClassParser(context);
parser.parse(sourceConfigurationClass);
//3.1向IoC容器中注册@Bean方法对应的BeanDefinition
loadBeanDefinitionsFromBeanMethods(parser.configurationClasses);
//3.2向IoC容器中注册ImportBeanDefinitionRegistrar导入的BeanDefinition
loadBeanDefinitionFromRegistrars(parser.configurationClasses);
}

递归函数parse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}

如何实现类AutoConfigurationImportSelector

SpringFactories机制

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

classpath中读取到所有jar包中的配置文件META-INF/spring.factories,然后根据指定的key从配置为念中解析出对应的value

SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中的配置类型信息加载到Spring容器,并执行类中定义的各种操作

EnableAutoConfiguration,是实现自动装配的重要注解。内部的实际实现是AutoConfigurationImportSelector类来实现,getAutoConfigurationEntry可以返回需要导入的自定义配置,将这些类加载到IOC容器中

在获取的导入配置中,首先会扫描所有的自动配置类,接着根据自定义配置中的ConditionalOnXXX注解做判断,决定是否将其放入最终的导入集合中

例如:

  • @ConditionalOnBean
  • @ConditionalOnClass
  • @ConditionalOnProperty

具体步骤:

  1. 通过@SpringBootConfiguration引入@EnableAutoConfiguration(负责启动自动配置功能)
  2. @EnableAutoConfiguration引入了@Import
  3. Spring容器启动时:加载IOC容器会解析@Import注解
  4. @Import导入了一个deferredImportSelector,它会使Spring的自动配置类的顺序在最后,这样方便我们扩展和覆盖
  5. 然后读取所有的/META-INF/spring.factories文件
  6. 筛选出所有以EnableAutoConfiguration.classkey的、符合条件的配置类,过滤出所有AutoConfigurationClass类型的类
  7. 最后通过@ConditionOnXXX排除无效的自动配置类

一句话总结,自动配置就是通过扫描jar包下面/META-INF/Spring.factories获取到自动配置,再根据@Conditional按需加载的配置类