SpringBoot自动配置(装配)流程

打印 上一主题 下一主题

主题 566|帖子 566|积分 1698

源码分析

SpringBoot自动配置流程


​        首先,我们要了解在@SpringBootApplication注解的内部,还具有@EnableAutoConfiguration,@SpringBootConfiguration,@ComponentScan三个主要注解。
  1. @SpringBootConfiguration  //标注该类是配置类,需要通过该类查找自动配置文件
  2. @EnableAutoConfiguration        //自动配置的关键注解 其内部就是执行自动配置的代码
  3. @ComponentScan(excludeFilters = {
  4.   //type : 要使用的筛选器类型 , classes 指定类型筛选器
  5.   //TypeExcludeFilter.class 筛选掉spirngBootApplication中被指定排除的配置类
  6.   @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),       
  7.   //AutoConfigurationExcludeFilter 将配置类与spirng.factories中的EnableAutoConfiguration对应的配置类进行对比匹配, 如果一致,会被排除掉
  8.         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }
  9. )        //扫描指定包的文件,将带有特定注解的类注入到Bean中
  10. public @interface SpringBootApplication {
  11. }
复制代码
@ComponentScan


  • @ComponentScan注解主要用来扫描我们项目中的所有被像@service ,@Repository , @Controller,@configuration 等注解修饰的类, 将其注入到我们的IOC容器中,其中也包括我们的自动配置的文件:
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Repeatable(ComponentScans.class)  //表示可以重复利用@ComponentScan注解
  5.       /**
  6.         *作用 : 可以扫描指定的包,如果未指定包范围,将从该注解标注类所在的包进行扫描,
  7.         *                           与XML形式的<context:component scan>不同的是 @componentScan没有Config属性(true         *        就开启了属性自动注入的功能,如果是false就是关闭属性自动注入的功能),因为使用
  8.         *  @ComponentScan则默认所有的类都进行自动注入,会将所有扫描到的组件注入到IOC容器中
  9.         */
  10. public @interface ComponentScan {
  11. }
复制代码
@SpringBootConfiguration


  • @SpringBootConfiguration 是SpringBoot替代@Configuration的注解,增加了自动找到配置的功能
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Configuration   //表示这是一个配置类 通过其间接了解到,@SpringBootApplication也是一个配置类
  5. /**
  6.   * 用作Spring的标准@Configuration注解的替代,以便可以自动找到配置
  7.   */
  8. public @interface SpringBootConfiguration {
  9. }
复制代码
@EnableAutoConfiguration


  • @EnableAutoConfiguration注解就是启动自动配置的关键注解,其内部使用了@import注解引入了一个AutoConfigurationImportSelector 自动配置类选择器
  1. @AutoConfigurationPackage //自动配置所在包注解,通过basePackages指定配置所在的包或者通过basePackageClasses指定基本包类,如果未指定,会默认注册指定注解类所在的包
  2. //AutoConfigurationImportSelector自动配置选择器,实现了ImportSelector接口,重写了selectImports方法,自动配置的具体实现就在其内部进行
  3. //ImportSelector接口作用 :根据给定的选择条件(通常是一个或多个注解属性)确定应导入哪个配置类。
  4. @Import(AutoConfigurationImportSelector.class)
  5. public @interface EnableAutoConfiguration {
  6. }
复制代码
​        在其内部重写了selectImports方法, 通过调用getAutoConfigurationEntry()方法根据传入的注解元数据,获取到自动配置类的实体,而后从实体中获取具体的配置信息,配置信息在实体内部是一个list集合,所以将其转化为String数组后返回。
  1. //为方便显示及理解,省略了该类实现的部分接口和具体的代码实现,需要了解可进入源码查看
  2. public class AutoConfigurationImportSelector implements DeferredImportSelector {
  3.   @Override
  4.         public String[] selectImports(AnnotationMetadata annotationMetadata) {
  5.         //AnnotationMetadata: 配置类的注解元数据,也就是配置类的注解信息
  6.   //调用getAutoConfigurationEntry()方法根据传入的注解信息,获取并返回自动配置类的实体
  7.                 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  8.     //从配置实体中获取具体的配置信息,返回的是一个list集合,而后通过toStringArray()方法转存到字符串数组中返回
  9.                 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  10.         }
  11. }
复制代码
getAutoConfigurationEntry()
  1. //可以先看下获取的大致流程,而后进入查看器方法内部的具体实现
  2. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  3.         //1.  从注解元数据中获取注解的相应属性,将相应属性存储到map中返回
  4.           //1.1AnnotationAttributes是一个Map集合,其继承了LinkedHashMap
  5.                 AnnotationAttributes attributes = getAttributes(annotationMetadata);
  6.           //2. 通过getCandidateConfigurations()方法根据注解元数据和注解的属性信息 获取应该进行自动配置的类名,可以理解为自动配置候选项
  7.                 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  8.         //2.1 通过removeDuplicates()方法对自动配置的类名进行去重处理
  9.   configurations = removeDuplicates(configurations);
  10.           //3. 根据注解元数据和注解属性获取到需排除配置项
  11.                 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  12.           //3.1检查是否有无效的排除类存在
  13.                 checkExcludedClasses(configurations, exclusions);
  14.           //3.2从自动配置候选项中删除需要排除的配置项
  15.                 configurations.removeAll(exclusions);
  16.           //4. 调用getConfigurationClassFilter()方法获取到获取配置的所有AutoConfigurationImportFilter的实现类(对spring.factories进行过滤的类),调用filter方法对配置文件进行筛选,而后返回需要自动配置的类
  17.                 configurations = getConfigurationClassFilter().filter(configurations);
  18.           //5. 根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件,最后根据多次过滤、判重返回配置类合集
  19.                 fireAutoConfigurationImportEvents(configurations, exclusions);
  20.           //6. 创建一个新的配置实体ConfigurationEntry并返回,包含需要配置项configurations,和被排除配置项exclusions
  21.                 return new AutoConfigurationEntry(configurations, exclusions);
  22.         }
复制代码
下面了解以下getAutoConfigurationEntry()内部调用的方法源码
从注解元数据中返回相应的属性信息


  • getAttributes(AnnotationMetadata annotationMetadata)
    1. /**
    2. * 从注解元数据中返回相应的属性信息。
    3. * param 注解元数据信息
    4. * return   注解元数据的属性信息 其本质是一个Map集合
    5. */
    6. protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
    7. /**
    8. * getAnnotationClass() 返回源注解类 -->EnableAutoConfiguration.class
    9. * getAnnotationClass().getName(); 获取注解类的完全限定类名
    10. */
    11.   String name = getAnnotationClass().getName();
    12. /**
    13. * metadata.getAnnotationAttributes(String annotationName,boolean classValuesAsString)
    14. * 作用: 检索给定注解的属性
    15. * @param1 要查找的注解类的完全限定类名
    16. * @param2 是否将类引用转换为String类名,以便作为返回Map中的值公开,而不是可能必须首先加载的类引用
    17. *
    18. *AnnotationAttributes.fromMap(@Nullable Map<String, Object> map);
    19. * 基于给定的集合返回AnnotationAttributes实例。如果该集合是AnnotationAttributes实例或其子类,它将被强制转换并立即返回,而无需创建新实例。否则,将通过将提供的映射传递给AnnotationAttributes的map)的构造函数来创建新实例。其参数是一个Map类型的注解属性数据源,也就是attrbuties
    20. */
    21.           AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    22.           /**
    23.            *Assert类 是一个协助验证参数的断言实用程序类,详细使用可以查看其源码
    24.          * Assert.notNull(@Nullable Object object, Supplier<String> messageSupplier)方法
    25.            * 作用 : 判断对象是不是null, 如果为null,报错提示
    26.            * param1 : 要进行判断的对象
    27.          * param2 : 如果为null,要给予返回的异常信息
    28.           */
    29.                 Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()+ " annotated with " + ClassUtils.getShortName(name) + "?");
    30.                 //返回注解元数据的属性信息map集合
    31.           return attributes;
    32.         }
    复制代码

获取应该进行自动配置的类名


  • getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes);
    1. /**
    2. *根据注解元数据和注解的属性信息 获取应该进行自动配置的类名,可以理解为自动配置的候选项(初选名单)
    3. *param1 元注解数据
    4. *param2 元注解数据的属性信息集合       
    5. *return List<String> 存储的数据就是应该继续宁自动配置的类名
    6. */
    7. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    8.                    //SpringFactoriesLoader是一个用于框架内部使用的通用工厂加载机制
    9.                 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    10.                                 getBeanClassLoader());
    11.                 Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    12.                                 + "are using a custom packaging, make sure that file is correct.");
    13.                 return configurations;
    14. }
    复制代码
对自动配置项进行去重处理


  • 1 configurations = removeDuplicates(configurations);对自动配置的类名进行去重处理
  1. //通过removeDuplicates()方法对自动配置的类名进行去重处理
  2. //利用Set集合数据不重复特性,将list集合存储到LinkedHashSet集合中进行去重处理,而后再将去重的结果存储到List集合中返回
  3. protected final <T> List<T> removeDuplicates(List<T> list) {
  4.                 return new ArrayList<>(new LinkedHashSet<>(list));
  5. }
复制代码
从自动配置项中筛选被排除配置项


  • configurations.removeAll(exclusions);
  1. //从自动配置候选项中筛选需排除配置项
  2. protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  3.           //创建一个需排除配置项集合excluded
  4.                 Set<String> excluded = new LinkedHashSet<>();
  5.           //从属性信息集合中获取到key为exclude的值,将其存储到excluded集合中
  6.                 excluded.addAll(asList(attributes, "exclude"));
  7.           //从属性信息集合中获取到key为excludeName的数据,返回的是一个字符串数组,返回后将其转化为List集合,存储到excluded集合中
  8.                 excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
  9.   /**
  10.    * getExcludeAutoConfigurationsProperty():
  11.    *返回 spring.autoconfigure.exclude 属性排除的自动配置
  12.    */
  13.           excluded.addAll(getExcludeAutoConfigurationsProperty());
  14.                 return excluded;
  15. }
  16. -----------------------------------------------------------------------------------------
  17. /*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
  18. -----------------------------------------------------------------------------------------
  19. //attributes.getStringArray("excludeName")
  20. public String[] getStringArray(String attributeName) {
  21.                 return getRequiredAttribute(attributeName, String[].class);
  22. }
复制代码
exclude 和excludeName 都是指定某些类在项目启动时不进行自动配置,其一般在@SpringBootApplication 中进行配置。
检查是否有无效的排除类存在


  • 1 configurations.removeAll(exclusions);
  1. //检查是否有无效的排除类存在
  2. private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
  3.         //创建一个用于存储无效配置项的集合               
  4.   List<String> invalidExcludes = new ArrayList<>(exclusions.size());
  5.         //循环需排除配置项
  6.   for (String exclusion : exclusions) {
  7.     //根据类的全限定名判断该类是否存在且可以被加载,并且 需排除配置项集合是否包含该类
  8.                         if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
  9.         //如果存在,且不再需排除配置项的集合中,将其添加到无效配置项集合中
  10.                                 invalidExcludes.add(exclusion);
  11.                         }
  12.                 }
  13.           //如果无效配置项集合不为空,说明存在无效配置项
  14.                 if (!invalidExcludes.isEmpty()) {
  15.       //处理无效配置项 --> 报错 IllegalStateException 无效状态异常
  16.                         handleInvalidExcludes(invalidExcludes);
  17.                 }
  18. }
  19. -----------------------------------------------------------------------------------------
  20. /*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
  21. -----------------------------------------------------------------------------------------
  22. /**
  23. *ClassUtils.isPresent() 根据类名称判断是否存在并且可以加载,如果类或其依赖项之一不存在或无法     加载返回false
  24. * param1 className 要检查的类的名称
  25. * param2 classLoader 要使用的类加载器(如果为null,表示默认的类加载器)
  26. */
  27. public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
  28.                 try {
  29.       //forName(类名称,类加载器) 用于替换Class.forName()方法, 并且还返回所提供名称的类实例
  30.                         forName(className, classLoader);
  31.                         return true;
  32.                 }
  33.                 catch (IllegalAccessError err) {
  34.                         throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
  35.                                         className + "]: " + err.getMessage(), err);
  36.                 }
  37.                 catch (Throwable ex) {
  38.                         // Typically ClassNotFoundException or NoClassDefFoundError...
  39.                         return false;
  40.                 }
  41. }
复制代码
从自动配置项中删除需要被排除的配置项


  • 2 configurations.removeAll(exclusions);
  1. /**
  2. *从自动配置候选项中删除需要排除的配置项  
  3. * 集合A.removeAll(集合B);作用就是从集合A数据项中删除掉集合B所包含的元素
  4. */
  5. configurations.removeAll(exclusions);
复制代码
创建配置类过滤器对配置项进行筛选过滤


  • configurations = getConfigurationClassFilter().filter(configurations);
  1. //通过getConfigurationClassFilter()获取所有AutoConfigurationImportFilter的实现类(对spring.factories进行过滤的类),而后调用filter方法对配置文件进行筛选,而后返回需要自动配置的类
  2. configurations = getConfigurationClassFilter().filter(configurations);
  3. -----------------------------------------------------------------------------------------
  4. /*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
  5. -----------------------------------------------------------------------------------------
  6. //获取配置类过滤器
  7. private ConfigurationClassFilter getConfigurationClassFilter() {
  8.   //this.configurationClassFilter当前类的配置类过滤器是不是为null
  9.                 if (this.configurationClassFilter == null) {
  10.       // 获取AutoConfigurationImportFilter过滤器的实现类集合
  11.       List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
  12.      
  13.       for (AutoConfigurationImportFilter filter : filters) {
  14.                                 invokeAwareMethods(filter);  //在监听器注入是有描述,两者使用的同一方法
  15.                         }
  16.       //实例化配置类过滤器 ,根据 类加载器和过滤器实现类实例化配置类过滤器
  17.       //ConfigurationClassFilter类内部含有类加载器和过滤器实现类集合的属性
  18.                         this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
  19.                 }
  20.           //返回配置类过滤器
  21.                 return this.configurationClassFilter;
  22. }
  23. //getAutoConfigurationImportFilters(); 获取AutoConfigurationImportFilter过滤器的实现类集合
  24. protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
  25.   /**
  26.    * List<T>  loadFacotries(Class<T> factoryType, @Nullable ClassLoader classLoader)
  27.    * 使用给定的类加载器从{"META-INF/spring.factories"}加载并实例化指定过滤器工厂的实现类
  28.    * 在结果返回之前会对结果集进行排序
  29.    * param1 表示工厂的接口或者抽象类,-->生成其子类
  30.          * param2 当前类的类加载器-->用于加载抽象类的实现类
  31.          * return 返回指定接口或者抽象类的实现类List集合
  32.          */
  33.           //返回AutoConfigurationImportFilter实现类的集合
  34.                 return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
  35.         }
  36. //configurations = getConfigurationClassFilter().filter(configurations);
  37. //过滤 自动配置项
  38. List<String> filter(List<String> configurations) {
  39.                         long startTime = System.nanoTime();
  40.                   //将将配置转化为字符串数组
  41.                         String[] candidates = StringUtils.toStringArray(configurations);
  42.                         boolean skipped = false;
  43.                  
  44.                         for (AutoConfigurationImportFilter filter : this.filters) {
  45.         //循环过滤条件与配置项进行一一匹配,剔除掉条件不成立的配置项
  46.                                 boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
  47.                                 for (int i = 0; i < match.length; i++) {
  48.                                         if (!match[i]) {
  49.                                                 candidates[i] = null;
  50.                                                 skipped = true;
  51.                                         }
  52.                                 }
  53.                         }
  54.                   //如果全都符合则直接返回配置项集合
  55.                         if (!skipped) {
  56.                                 return configurations;
  57.                         }
  58.                   //创建结果集集合
  59.                         List<String> result = new ArrayList<>(candidates.length);
  60.                         for (String candidate : candidates) {
  61.                                 //配置项不为null就添加到配置类中
  62.         if (candidate != null) {
  63.                                         result.add(candidate);
  64.                                 }
  65.                         }
  66.                   //返回结果
  67.                         return result;
  68.                 }
  69.         }
复制代码
创建配置类监听器对自动配置进行监听


  • fireAutoConfigurationImportEvents(configurations, exclusions);
  1. //根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件,最后根据多次过滤、判重返回配置类合集
  2. private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
  3.           //从{ "META-INF/spring.factories"}加载并实例化自动配置类监听器                          AutoConfigurationImportListener的实现类集合
  4.                 List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
  5.           //如果监听器不为空的话
  6.                 if (!listeners.isEmpty())
  7.        //创建fireAutoConfigurationImportEvents监听事件
  8.                   AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
  9.                   //循环遍历 判断listener是否是 Aware 通过Aware接口 实现对bean各阶段的监听
  10.                         for (AutoConfigurationImportListener listener : listeners) {
  11.         //通过Aware类的实现类对监听器进行配置 -->解这一模块,可以重点关注以下Aware接口
  12.                                 invokeAwareMethods(listener);  
  13.                                 //进行自动配置的导入 event 到自动配置时进行的事件-->对自动配置的监听
  14.         listener.onAutoConfigurationImportEvent(event);
  15.                         }
  16.                 }
  17. }
  18. //根据Aware类对bean的各阶段进行监听配置
  19. private void invokeAwareMethods(Object instance) {
  20.           //判断监听器是否是Aware或其实现类
  21.                 if (instance instanceof Aware) {
  22.                
  23.       if (instance instanceof BeanClassLoaderAware) {
  24.        /**
  25.                          * BeanClassLoaderAware 允许bean知道bean的回调ClassLoader,即当前bean工厂用来加载bean类的类加载器。这主要是由框架类来实现的,这些框架类必须通过名称来获取应用程序类,尽管它们本身可能
  26. 从共享类加载器加载的。
  27.                          * 方法 setBeanClassLoader(ClassLoader classLoader);
  28.                          *                将bean的类加载器 提供给bean实例的回调。
  29.                          *                 作用范围: 在填充普通bean属性之后,初始化回调之前调用
  30.                          */
  31.                                 ((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
  32.                         }
  33.                         if (instance instanceof BeanFactoryAware) {
  34.       /**
  35.                          * BeanFactoryAware 表示接口知道其拥有的{BeanFactory}的bean实现。
  36.                          *注意 :大多数的bean都可以通过属性注入和构造注入接收对bean的引用
  37.                          *方法 :setBeanFactory(BeanFactory beanFactory)
  38.                          *                向bean实例提供拥有工厂的回调。
  39.                          *                 作用范围: 在填充普通bean属性之后,初始化回调之前调用,
  40.                          */
  41.         
  42.                                 ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
  43.                         }
  44.                         if (instance instanceof EnvironmentAware) {
  45.       /**
  46.                          * EnvironmentAware 表示该接口可以收到其运行环境的通知
  47.                          *方法 :setEnvironment(Environment environment);
  48.                          *                设置此组件的运行环境
  49.                          */
  50.         
  51.                                 ((EnvironmentAware) instance).setEnvironment(this.environment);
  52.                         }
  53.                         if (instance instanceof ResourceLoaderAware) {
  54.               /**
  55.                          * ResourceLoaderAware 接口,该接口可以收到该对象运行的ResourceLoader资源封装类加载器
  56.                          *方法 :setResourceLoader(ResourceLoader resourceLoader);
  57.                          *作用 :设置此对象运行的ResourceLoader。可能是一个ResourcePatternResolver
  58.                          *作用范围: 在填充普通bean属性之后,初始化回调之前调用
  59.                          */
  60.         
  61.                                 ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
  62.                         }
  63.                 }
  64. }
复制代码
创建新的配置实体后返回SelectImports方法体内


  • return new AutoConfigurationEntry(configurations, exclusions);
  1. 根据需要配置项和被排除项实例化新的配置实体,并返回
  2. AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
  3.                         this.configurations = new ArrayList<>(configurations);
  4.                         this.exclusions = new HashSet<>(exclusions);
  5. }
复制代码
将配置实体中的配置信息转化为字符串数组返回,完成注入
  1. //获取最终要导入的配置实体
  2. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  3. //从配置实体中获取具体的配置信息,返回的是一个list集合,而后通过toStringArray()方法转存到字符串数组中返回
  4.                 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
复制代码
本笔记个人查看源码时根据立即理解缩写,如有错误可留言告知,谢谢

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表