-
自动配置概念: 根据我们添加的jar包依赖,会自动将一些配置类的bean注册进ioc容器,在需要的地方使用@autowired或者@resource等注解来使用它。
-
注解介绍:(自动配置原理主要在EnableAutoConfiguration注解中)
-
@SpringBootApplication注解是一个组合注解,前面 4 个是注解的元数据信息,主要看后面3个注解:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个核心注解,关于这三个核心注解的相关说明具体如下。
- @SpringBootConfiguration:springboot的配置类,标注在某个类上,表示这是一个springboot的配置类。 进入该注解,如下图所示
@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封装命名
- @EnableAutoConfiguration:是一个组合注解源代码如图
spring中有很多以enable开头的注解,其作用就是借助@Import来手机注册特定场景的Bean,并加载到容器中。@EnableAutoconfiguration就是借助@Import注解来收集所有所有符合自动配置条件的bean并加入到spring容器中。
注解一:@AutoConfigurationPackage源代码如下@Import(AutoConfigurationPackages.Registrar.class) ,它就是将 Registrar 这个组件类导入 到容器中,可查看 Registrar 类中 registerBeanDefinitions 方法
AutoConfigurationPackages.Registrar这个类就干一个事,注册一个 Bean,这个Bean就是 org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages(根目录),它有 一个参数,这个参数是使用了 @AutoConfigurationPackage 这个注解的类所在的包路径,保存自动配置 类以供之后的使用,比如给 JPA entity 扫描器用来扫描开发人员通过注解 @Entity定义的 entity 类。
注解二:@Import(AutoConfigurationImportSelector.class)
将AutoConfigurationImportSelector 这个类导入到 Spring 容器中, AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。可以看到 AutoConfigurationImportSelector 重点是实现了 DeferredImportSelector 接口和各种Aware接口,然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。
其不光实现了ImportSelector接口,还实现了很多其它的 Aware 接口,分别表示在某个时机会被回调。 确定自动配置实现逻辑的入口方法:
跟自动配置逻辑相关的入口方法在DeferredImportSelectorGrouping 类的 getImports方法处。标【1】 处的的代码是我们分析的重中之重,自动配置的相关的绝大部分逻辑全在这里了。主要做的事情就是在 this.group 即 AutoConfigurationGroup 对象的 process 方法中,传入的 AutoConfigurationImportSelector对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,就是这么个事情。
注:
AutoConfigurationGroup:是AutoConfigurationImportSelector的内部类,主要用来处理自动配 置相关的逻辑,拥有process和selectImports方法,然后拥有entries和
autoConfigurationEntries集合属性,这两个集合分别存储被处理后的符合条件的自动配置类,我们知道这些就足够了;AutoConfigurationImportSelector:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配置类;
再进入到AutoConfigurationImportSelector$AutoConfigurationGroup的pross方法:通过图中我们可以看到,跟自动配置逻辑相关的入口方法在process方法中
上面代码中我们再来看标 【1】 的方法 getAutoConfigurationEntry ,这个方法主要是用来获取自动配置类有关,承担了自动配置的主要逻辑。
再看上图代码中【1】getAutoConfigurationEntry这个方法中有一个重要方法 loadFactoryNames ,这个方法是让 SpringFactoryLoader 去加载一些组件的名字
继续点开 loadFactoryNames 方法
从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。spring.factories里面保存着springboot的默认提供的自动配置类
AutoConfigurationEntry 方法主要做的事情就是获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费。我们下面总结下 AutoConfigurationEntry 方法主要做的事情:
【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配 置类如图所示。
【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类 排除掉;
【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些
不符合条件的自动配置类;
【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉
ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;
【5】 最后再将符合条件的自动配置类返回。
AutoConfigurationImportSelector 的 filter 方法主要做的事情就是调用
与此同时了解下AutoConfigurationImportSelector 的 filter 方法:AutoConfigurationImportFilter 接口的 match 方法来判断每一个自动配置类上的条件注解(若有的话) @ConditionalOnClass , @ConditionalOnBean或@ConditionalOnWebApplication是否满足 条件,若满足,则返回true,说明匹配,若不满足,则返回false说明不匹配。我们现在知道 AutoConfigurationImportSelector 的filter方法主要做了什么事情就行了。
总结下SpringBoot自动配置的原理,主要做了以下事情
(1). 从spring.factories配置文件中加载自动配置类;
(2). 加载的自动配置类中排除掉 @EnableAutoConfiguration 注解的 exclude 属性指定的自动配置类;
(3). 然后再用 AutoConfigurationImportFilter 接口去过滤自动配置类是否符合其标注注解(若有标注的话) @ConditionalOnClass , @ConditionalOnBean 和@ConditionalOnWebApplication 的条件,若都符合的话则返回匹配结果;
(4). 然后触发 AutoConfigurationImportEvent 事件,告诉ConditionEvaluationReport条件评估报告器对象来分别记录符合条件和 exclude 的自动配置类。
(5). 最后spring再将最后筛选后的自动配置类导入IOC容器中 - ComponentScan注解 主要是从定义的扫描路径中,找出标识了需要装配的类自动装配到spring 的bean容器中。
常用属性如下:basePackages、value:指定扫描路径,如果为空则以@ComponentScan注解的类所在的包为基本的扫描路径
basePackageClasses:指定具体扫描的类
includeFilters:指定满足Filter条件的类
excludeFilters:指定排除Filter条件的类
includeFilters和excludeFilters 的FilterType可选:ANNOTATION=注解类型 默认、
ASSIGNABLE_TYPE(指定固定类)、ASPECTJ(ASPECTJ类型)、REGEX(正则表达式)、CUSTOM(自定义类 型),自定义的Filter需要实现TypeFilter接口
@ComponentScan的配置如下:借助excludeFilters将TypeExcludeFillter及FilterType这两个类进行排除当前@ComponentScan注解没有标注basePackages及value,所以扫描路径默认为@ComponentScan 注解的类所在的包为基本的扫描路径(也就是标注了@SpringBootApplication注解的项目启动类所在 的路径)