1.B.1、@AutoConfigurationPackage注解

第二个注解里面的第一个分析的注解@AutoConfigurationPackage(所以是1.B.1)

1、点进去看@AutoConfigurationPackage

image.png

2、分析该注解的源码

查看@AutoConfigurationPackage注解内部源码信息,核心代码具体如下:

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. // Spring底层注解@Import,给容器导入一个组件
  6. // 导入AutoConfigurationPackages.Registrar.class中注册的组件
  7. @Import(AutoConfigurationPackages.Registrar.class)
  8. public @interface AutoConfigurationPackage {
  9. String[] basePackages() default {};
  10. Class<?>[] basePackageClasses() default {};
  11. }

从上述源码可以看出,@AutoConfigurationPackage注解的功能是由@lmport注解实现的,它是Spring
框架的底层注解,它的作用就是给容器中导入某个组件类,例如

@lmport(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类导入到容器中,

3、点进Registrar

image.png

4、给registerBeanDefinitions方法打上断点,并debug进来

可查看Registrar类中registerBeanDefinitions方法,这个方法就是导入组件类的具体实现:
1、先打断点
image.png
2、debug启动
image.png
3、当代码停在断点处,点击执行
对 new PackageImport(metadata).getPackageName() 进行检索,看看其结果是什么?

image.png
image.png

4、点进register
image.png
//如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去

从上述源码可以看出,在Registrar类中有一个registerBeanDefinitions()方法,使用Debug模式启动项目,可以看到选中的部分就是com.lagou。也就是说,
@AutoConfigurationPackage注解的主要作用就是将主程序类所在包及所有子包下的组件到扫描到spring容器中。
因此在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位置,然后在根目录位置内部建立子包和类进行业务开发,这样才能够保证定义的类能够被组件扫描器扫描

AutoConfigurationPackages.Registrar这个类就干一个事,
注册一个 Bean ,这个 Bean 就是 org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages ,它有 一个参数,这个参数是使用了 @AutoConfigurationPackage 这个注解的类所在的包路径,保存自动配置 类以供之后的使用,比如给 JPA entity 扫描器用来扫描开发人员通过注解 @Entity 定义的 entity 类。 (???)

看了上面只是发现了一个注册一个bean对象而已,那就继续分析它 的第二个注解

1.B.2、@Import({AutoConfigurationImportSelector.class}):

将AutoConfigurationImportSelector这个组件类导入到spring容器中,
AutoConfigurationImportSelector 可以帮助 springboot应用 将所有符合条件的@Configuration配置都加载到 当前SpringBoot创建并使用的loC容器(ApplicationContext)中

那么如果想知道SpringBoot启动过程中,都会对那些配置类进行自动装配的话,就必须要看这个类

1、查看结构图

查看结构图:如果快捷键Crtl + Alt + U 失效的话,右键类 -> Diagrams -> Show Diagram Popup
image.png

可以看到 AutoConfigurationImportSelector 重点是实现了 DeferredImportSelector 接口和各种 Aware 接口,然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。 其不光实现了 ImportSelector 接口,还实现了很多其它的 Aware 接口,一旦实现Aware 接口,那么在执行的过程中,就会在某个时机会被回调。 在回调中,拿到 Spring的一些组件对象。如继承了BeanFactoryAware接口,就会在回到中拿到BeanFactory这个对象
拿到这些组件对象做啥呢?接下来进行分析。

2、点进AutoConfigurationImportSelector类

这个类就是整个自动配置最核心的部分,

第一,实现了很多Aware接口
image.png

第二实现了ImportSelector接口
image.png
image.png

3、确定自动配置实现逻辑的入口方法:

跟自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法处, 因此我们就从 DeferredImportSelectorGrouping 类的 getImports 方法来开始分析SpringBoot的自动配置源码好了。 (在 ConfigurationClassParser.java 类 )

注意分析目的是: 在执行getImports() 方法时 ,如何去调用到AutoConfigurationImportSelector

现在是先清楚,是如何调用到AutoConfigurationImportSelector这个方法的。

4、全局搜索DeferredImportSelectorGrouping,先进到这个类先。

image.png

5、先看一下 getImports 方法代码:

  1. /**遍历DeferredImportSelectorHolder对象集合deferredImports,deferredImports集合
  2. 装了各种ImportSelector,当然这里装的是AutoConfigurationImportSelector
  3. */
  4. public Iterable<Group.Entry> getImports() {
  5. for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
  6. // 【1】,利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定
  7. //导入哪些配置类(这个是我们分析的重点,自动配置的逻辑全在这了)
  8. this.group.process(deferredImport.getConfigurationClass().getMetadata(),
  9. deferredImport.getImportSelector());
  10. }
  11. // 【2】,经过上面的处理后,然后再进行选择导入哪些配置类
  12. return this.group.selectImports();
  13. }

标 【1】 处的的代码是我们分析的重中之重,自动配置的相关的绝大部分逻辑全在这里了。那么 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()) ;
主要做的事情就是在 this.group 即 AutoConfigurationGroup 对象的 process 方法中,传入的 AutoConfigurationImportSelector 对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,就是这么个事情。

image.png

  1. 注:
  2. AutoConfigurationGroup:是AutoConfigurationImportSelector的内部类,主要用来处理自动配
  3. 置相关的逻辑,拥有processselectImports方法,然后拥有entries
  4. autoConfigurationEntries集合属性,这两个集合分别存储被处理后的符合条件的自动配置类,我们知道
  5. 这些就足够了;
  6. AutoConfigurationImportSelector:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配
  7. 置类;
  8. metadata:标注在SpringBoot启动类上的@SpringBootApplication注解元数据
  9. 标【2】的this.group.selectImports的方法主要是针对前面的process方法处理后的自动配置类再进一
  10. 步有选择的选择导入

6、手动进入process

image.png

image.png

7、进到AutoConfigurationGroup#process方法

再进入到AutoConfigurationGroup类是AutoConfigurationImportSelector的内部类
image.png

通过图中我们可以看到,跟自动配置逻辑相关的入口方法在process方法中

8、 分析getAutoConfigurationEntry方法进行自动配置的主要逻辑

上面代码中我们再来看标 【1】 的方法 getAutoConfigurationEntry ,这个方法主要是用来获取自动 配置类有关,承担了自动配置的主要逻辑。直接上代码:

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
  2. AnnotationMetadata annotationMetadata) {
  3. // 获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费
  4. if (!isEnabled(annotationMetadata)) {
  5. return EMPTY_ENTRY;
  6. }
  7. // 获得@Congiguration标注的Configuration类即被审视introspectedClass的注解数据,
  8. // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
  9. // 将会获取到exclude = FreeMarkerAutoConfiguration.class和excludeName=""的注解数据
  10. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  11. // 【1】得到spring.factories文件配置的所有自动配置类
  12. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  13. // 利用LinkedHashSet移除重复的配置类
  14. configurations = removeDuplicates(configurations);
  15. // 得到要排除的自动配置类,比如注解属性exclude的配置类
  16. // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
  17. // 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据
  18. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  19. // 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
  20. checkExcludedClasses(configurations, exclusions);
  21. // 【2】将要排除的配置类移除
  22. configurations.removeAll(exclusions);
  23. // 【3】因为从spring.factories文件获取的自动配置类太多,如果有些不必要的自动配置类都加载
  24. //进内存,会造成内存浪费,因此这里需要进行过滤
  25. // 注意这里会调用AutoConfigurationImportFilter的match方法来判断是否符合
  26. configurations = filter(configurations, autoConfigurationMetadata);
  27. /*【4】获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件,
  28. 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
  29. 该事件什么时候会被触发?--> 在刷新容器时调用invokeBeanFactoryPostProcessors后置处
  30. 理器时触发*/
  31. fireAutoConfigurationImportEvents(configurations, exclusions);
  32. // 【5】将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
  33. return new AutoConfigurationEntry(configurations, exclusions);
  34. }

9、继续进入getCandidateConfigurations方法

这个全路径是可以在SpringBoot启动中,可以进行自定义配置的,配置类。
image.png

10、继续进入loadFactoryNames方法

image.png

11、看loadFactoryNames方法

image.png

12、加载配置文件META-INF/spring.factories

image.png

13、可以全局搜索配置文件

image.png
随便点开一个
image.png
一个key对应多个值,所谓的值都是工厂类的全路径。
这些值也就是在SpringBoot启动过程中,可以来进行自动装配的自动配置类。

14、那么其作用是干啥

先随便进入一个类
image.png

image.png
如果程序中用到此对象,就不需要new 了,就直接注入进来就能使用了。

15、从11点执行完后得到结果集

image.png
image.png

16、去重

那么多配置类全部都需要加载进来嘛?
怎么可能,首先去重
image.png

17、通过filter进行过滤

image.png

18、小总结一下

从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。 spring.factories里面保存着springboot的默认提供的自动配置类。

@EnableAutoConfiguration就是从classpath中搜寻META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(JavaRefletion)实例化为对应的标注了@Configuration的JavaConfig形式的配置类,并加载到IOC容器中

AutoConfigurationEntry 方法主要做的事情就是获取符合条件的自动配置类,避免加载不必要的自 动配置类从而造成内存浪费。我们下面总结下 AutoConfigurationEntry 方法主要做的事情:
【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配 置类如图所示。
【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类 排除掉;
【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些 不符合条件的自动配置类;
【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;
【5】 最后再将符合条件的自动配置类返回

以刚刚的项目为例,在项目中加入了Web环境依赖启动器,对应的WebMvcAutoConfiguration自动配置类就会生效,打开该自动配置类会发现,在该配置类中通过全注解配置类的方式对SpringMVC运行所需环境进行了默认配置,包括默认前缀、默认后缀、视图解析器、MVC校验器等。而这些自动配置类的本质是传统SpringMVC框架中对应的XML配置文件,只不过在SpringBoot中以自动配置类的形式进行了预先配置。因此,在SpringBoot项目中加入相关依赖启动器后,基本上不需要任何配置就可以运行程序,当然,我们也可以对这些自动配置类中默认的配置进行更改

19、 调用 match 方法进行过滤

AutoConfigurationImportSelector 的 filter 方法主要做的事情就是调用 AutoConfigurationImportFilter 接口的 match 方法来判断,每一个自动配置类上的条件注解 @ConditionalOnClass , @ConditionalOnBean 或 @ConditionalOnWebApplication (若有的话)判断是否满足 条件,
若满足,则返回true,说明匹配,
若不满足,则返回false说明不匹配。
我们现在知道 AutoConfigurationImportSelector 的 filter 方法主要做了什么事情就行了,现在先 不用研究的过深

整个filter源码如下:

  1. private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
  2. long startTime = System.nanoTime();
  3. // 将从spring.factories中获取的自动配置类转出字符串数组
  4. String[] candidates = StringUtils.toStringArray(configurations);
  5. // 定义skip数组,是否需要跳过。注意skip数组与candidates数组顺序一一对应
  6. boolean[] skip = new boolean[candidates.length];
  7. boolean skipped = false;
  8. // getAutoConfigurationImportFilters方法:拿到OnBeanCondition,OnClassCondition和OnWebApplicationCondition
  9. // 然后遍历这三个条件类去过滤从spring.factories加载的大量配置类
  10. for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
  11. // 调用各种aware方法,将beanClassLoader,beanFactory等注入到filter对象中,
  12. // 这里的filter对象即OnBeanCondition,OnClassCondition或OnWebApplicationCondition
  13. invokeAwareMethods(filter);
  14. // 判断各种filter来判断每个candidate(这里实质要通过candidate(自动配置类)拿到其标注的
  15. // @ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication里面的注解值)是否匹配,
  16. // 注意candidates数组与match数组一一对应
  17. /**********************【主线,重点关注】********************************/
  18. boolean[] match = filter.match(candidates, autoConfigurationMetadata);
  19. // 遍历match数组,注意match顺序跟candidates的自动配置类一一对应
  20. for (int i = 0; i < match.length; i++) {
  21. // 若有不匹配的话
  22. if (!match[i]) {
  23. // 不匹配的将记录在skip数组,标志skip[i]为true,也与candidates数组一一对应
  24. skip[i] = true;
  25. // 因为不匹配,将相应的自动配置类置空
  26. candidates[i] = null;
  27. // 标注skipped为true
  28. skipped = true;
  29. }
  30. }
  31. }
  32. // 这里表示若所有自动配置类经过OnBeanCondition,OnClassCondition和OnWebApplicationCondition过滤后,全部都匹配的话,则全部原样返回
  33. if (!skipped) {
  34. return configurations;
  35. }
  36. // 建立result集合来装匹配的自动配置类
  37. List<String> result = new ArrayList<>(candidates.length);
  38. for (int i = 0; i < candidates.length; i++) {
  39. // 若skip[i]为false,则说明是符合条件的自动配置类,此时添加到result集合中
  40. if (!skip[i]) {
  41. result.add(candidates[i]);
  42. }
  43. }
  44. // 打印日志
  45. if (logger.isTraceEnabled()) {
  46. int numberFiltered = configurations.size() - result.size();
  47. logger.trace("Filtered " + numberFiltered + " auto configuration class in "
  48. + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
  49. }
  50. // 最后返回符合条件的自动配置类
  51. return new ArrayList<>(result);
  52. }

20、关于条件注解的讲解

上面第19点,执行过程中,会用到Spring提供的以下注解,来判断是否生效。
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册 bean。 @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式 的条件判断。 @ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。 @ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。 @ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。 @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。 @ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首 选的Bean时触发实例化。

image.png
生效后,旗下所有的标注Bean注解的都会进行生成对应实例对象,存到ioc容器中。

image.png

21、将返回的结果进行封装

整个8-20都是分析第八点的。这整个目的就是为了让那些配置类真正的生效,生成Bean对象,存入容器中
image.png

到此,也就分析完了第5点的process方法。

22、那么接下来就要分析第五点的selectImports 方法

image.png

23、分析其源码

image.png

  1. @Override
  2. public Iterable<Entry> selectImports() {
  3. if (this.autoConfigurationEntries.isEmpty()) {
  4. return Collections.emptyList();
  5. }
  6. // 这里得到所有要排除的自动配置类的set集合
  7. Set<String> allExclusions = this.autoConfigurationEntries.stream()
  8. .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
  9. // 这里得到经过滤后所有符合条件的自动配置类的set集合
  10. Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
  11. .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
  12. .collect(Collectors.toCollection(LinkedHashSet::new));
  13. // 移除掉要排除的自动配置类
  14. processedConfigurations.removeAll(allExclusions);
  15. // 对标注有@Order注解的自动配置类进行排序,
  16. return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
  17. .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
  18. .collect(Collectors.toList());
  19. }

可以看到, selectImports 方法主要是针对经过排除掉 exclude 的和被 AutoConfigurationImportFilter 接口过滤后的满足条件的自动配置类再进一步排除 exclude 的自 动配置类,然后再排序

24、最后,我们再总结下SpringBoot自动配置的原理,主要做了以下事情:

  1. 从spring.factories配置文件中加载自动配置类;
    2. 加载的自动配置类中排除掉 @EnableAutoConfiguration 注解的 exclude 属性指定的自动配置 类;
    3. 然后再用 AutoConfigurationImportFilter 接口去过滤自动配置类是否符合其标注注解(若有 标注的话) @ConditionalOnClass , @ConditionalOnBean 和 @ConditionalOnWebApplication 的条件,若都符合的话则返回匹配结果;
    4. 然后触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评 估报告器对象来分别记录符合条件和 exclude 的自动配置类。
    5. 最后spring再将最后筛选后的自动配置类导入IOC容器中

25、总结,springboot底层实现自动配置的步骤是:

  1. springboot应用启动;
  2. @SpringBootApplication起作用;
  3. @EnableAutoConfiguration;
  4. @AutoConfigurationPackage:这个组合注解主要是@Import(AutoConfigurationPackages.Registrar.class),它通过将 Registrar类导入到容器中,而Registrar类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的容器中;
  5. @lmport(AutoConfigurationImportSelector.class):它通过将AutoConfgurationImportSelector类导入到容器中,AutoConfigurationImportSelector类作用是通过selectImports方法执行的过程中,会使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories进行加载,实现将配置类信息交给SpringFactory加载器进行一系列的容器创建过程

image.png

26、 以 HttpEncodingAutoConfiguration ( Http 编码自动配置)为例解释自动配置原理

  1. // 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
  2. @Configuration(proxyBeanMethods = false)
  3. // 启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;
  4. @EnableConfigurationProperties(ServerProperties.class)
  5. // Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效。
  6. // 判断当前应用是否是web应用,如果是,当前配置类生效。并把HttpEncodingProperties加入到 ioc 容器中
  7. @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
  8. // 判断当前项目有没有这个CharacterEncodingFilter : SpringMVC中进行乱码解决的过滤器
  9. @ConditionalOnClass(CharacterEncodingFilter.class)
  10. // 判断配置文件中是否存在某个配置 spring.http.encoding.enabled 如果不存在,判断也是成立的
  11. // matchIfMissing = true 表示即使我们配置文件中不配置 spring.http.encoding.enabled=true,也是默认生效的
  12. @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
  13. public class HttpEncodingAutoConfiguration {
  14. // 它已经和SpringBoot配置文件中的值进行映射了
  15. private final Encoding properties;
  16. // 只有一个有参构造器的情况下,参数的值就会从容器中拿
  17. public HttpEncodingAutoConfiguration(ServerProperties properties) {
  18. this.properties = properties.getServlet().getEncoding();
  19. }
  20. @Bean //给容器中添加一个组件,这个组件中的某些值需要从properties中获取
  21. @ConditionalOnMissingBean //判断容器中没有这个组件
  22. public CharacterEncodingFilter characterEncodingFilter() {
  23. CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
  24. filter.setEncoding(this.properties.getCharset().name());
  25. filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
  26. filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
  27. return filter;
  28. }
  29. @Bean
  30. public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
  31. return new LocaleCharsetMappingsCustomizer(this.properties);
  32. }
  33. static class LocaleCharsetMappingsCustomizer
  34. implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
  35. private final Encoding properties;
  36. LocaleCharsetMappingsCustomizer(Encoding properties) {
  37. this.properties = properties;
  38. }
  39. @Override
  40. public void customize(ConfigurableServletWebServerFactory factory) {
  41. if (this.properties.getMapping() != null) {
  42. factory.setLocaleCharsetMappings(this.properties.getMapping());
  43. }
  44. }
  45. @Override
  46. public int getOrder() {
  47. return 0;
  48. }
  49. }
  50. }

根据当前不同的条件判断,决定这个配置类是否生效。 一旦这个配置类生效,这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的 properties 类中获取的,这些类里面的每一个属性又是和配置文件绑定的。

  1. # 我们能配置的属性都是来源于这个功能的properties类
  2. spring.http.encoding.enabled=true
  3. spring.http.encoding.charset=utf-8
  4. spring.http.encoding.force=true
  1. public class Encoding {
  2. public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
  3. }

27、精髓

  1. SpringBoot 启动会加载大量的自动配置类
    2. 我们看我们需要实现的功能有没有 SpringBoot 默认写好的自动配置类
    3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们有我们要用的组件,我们就不需要 再来配置了)
    4. 给容器中自动配置类添加组件的时候,会从 properties 类中获取某些属性,我们就可以在配置文 件中指定这些属性的值。
    xxxAutoConfiguration :自动配置类,用于给容器中添加组件从而代替之前我们手动完成大量繁 琐的配置。 xxxProperties : 封装了对应自动配置类的默认属性值,如果我们需要自定义属性值,只需要根据 xxxProperties 寻找相关属性在配置文件设值即可。