千家信息网

如何理解springboot2.0.6中META-INF/spring.factories通过系统加载类获取对应的class的全限定名称

发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,这篇文章将为大家详细讲解有关如何理解springboot2.0.6中META-INF/spring.factories通过系统加载类获取对应的class的全限定名称,文章内容质量较高,因此小编分享给大
千家信息网最后更新 2025年01月23日如何理解springboot2.0.6中META-INF/spring.factories通过系统加载类获取对应的class的全限定名称

这篇文章将为大家详细讲解有关如何理解springboot2.0.6中META-INF/spring.factories通过系统加载类获取对应的class的全限定名称,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

在 SpringBoot中是通过getSpringFactoriesInstances(Class type)方法获取所有classpath下面的META-INF/spring.factories文件,然后根据type值找到对应的 class 的全限定名称列表。我来分析一下getSpringFactoriesInstances(Class type)方法是如何工作的

源码解析

getSpringFactoriesInstances()方法

public class SpringApplication {   private  Collection getSpringFactoriesInstances(Class type) {                return getSpringFactoriesInstances(type, new Class[] {});   }    // 获取Spring工厂   private  Collection getSpringFactoriesInstances(Class type,          Class[] parameterTypes, Object... args) {        // 获取ClassLoader       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();       // Use names and ensure unique to protect against duplicates        // 定义class数组,即返回值 names 是所有 classpath 下面的 META-INF/spring.factories 中定义的父节点(图2)       Set names = new LinkedHashSet<>(         SpringFactoriesLoader.loadFactoryNames(type, classLoader));        // 内部循环初始化 names的构造函数,获取实例实例对象(图2)       List instances = createSpringFactoriesInstances(type, parameterTypes,         classLoader, args, names);       AnnotationAwareOrderComparator.sort(instances);       return instances;   }   // 创建Spring工厂实例   private  List createSpringFactoriesInstances(Class type,      Class[] parameterTypes, ClassLoader classLoader, Object[] args,      Set names) {      List instances = new ArrayList<>(names.size());       // 循环处理names的值      for (String name : names) {          try { //( 图3)             //通过指定的classloader加载对应的类获取对应的Class对象             Class instanceClass = ClassUtils.forName(name, classLoader);             Assert.isAssignable(type, instanceClass);             Constructor constructor = instanceClass               .getDeclaredConstructor(parameterTypes);             //创建一个实例             T instance = (T) BeanUtils.instantiateClass(constructor, args);             instances.add(instance);          } catch (Throwable ex) {             throw new IllegalArgumentException(               "Cannot instantiate " + type + " : " + name, ex);         }      }      return instances;   }}

getSpringFactoriesInstances方法通过SpringFactoriesLoader类的loadFactoryNames方法获取系统加载类去所有classpath下面的 META-INF/spring.factories文件中获取对应的 class 的全限定名称结合,在执行createSpringFactoriesInstances方法遍历该集合对进行循环创建实例。然后返回实例对象集合。

  • names 为获取所有 classpath 下面的 META-INF/spring.factories文件中applicationContextInitializer相关加载类的值

  • instances 为遍历names创建的Spring工厂实例列表

以getSpringFactoriesInstances(ApplicationContextInitializer.class)为例进行debug分析

图1-1和图1-2标记出所有classpath下面的 META-INF/spring.factories文件中ApplicationContextInitializer.class对应的所有全限定名称

(图1-1)

(图1-2)

(图2)根据类名"applicationContextInitializer"获取 spring.factories文件中applicationContextInitializer相关的工厂类,并进行初始化)

( 图3) 根据类名进行初始化创建实例

在SpringApplication类的getSpringFactoriesInstances方法中进入SpringFactoresLoader类的loadFactoryNames方法

public abstract class SpringFactoriesLoader {   public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";   private static final Map> cache = new ConcurrentReferenceHashMap<>();    // 加载工厂   public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {      // 获取类名称(图4)      String factoryClassName = factoryClass.getName();      // 根据类名称获取需要加载的工厂类名称      return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());   }    // 通过加载所有 classpath 下面的 META-INF/spring.factories文件,扫描加载类,(图8)   private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {        // 从cache获取实例的结果集 当是Null表示当前的cache是空的;cache 实现 new ConcurrentReferenceHashMap<>()      MultiValueMap result = cache.get(classLoader);      if (result != null) {         return result;      }      try {          // 获取所有 classpath 下面的 META-INF/spring.factories 中的资源 urls(图5)          // 当classLoader为非空的时候调用getResouurces方法获取          // 当classLoader为空的时候调用ClassLoader.getSystemResouurces方法获取         Enumeration urls = (classLoader != null ?               classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :               ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));         result = new LinkedMultiValueMap<>();          // 循环处理 urls中的元素,获取元素          while (urls.hasMoreElements()) {             // 获取元素 url地址(图6)             URL url = urls.nextElement();            UrlResource resource = new UrlResource(url);             // 解析文件 把文件变成配置属性(图6)            Properties properties = PropertiesLoaderUtils.loadProperties(resource);             // 循环解析并把结果放入result            for (Map.Entry entry : properties.entrySet()) {                // 类名列表(图7)               List factoryClassNames = Arrays.asList(                     StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));               result.addAll((String) entry.getKey(), factoryClassNames);            }         }          // 缓存类加载器和文件解析器的结果集         cache.put(classLoader, result);         return result;      }      catch (IOException ex) {         throw new IllegalArgumentException("Unable to load factories from location [" +               FACTORIES_RESOURCE_LOCATION + "]", ex);      }   }}

以loadFactoryNames(ApplicationContextInitializer.class,classLoader)为例进行debug分析

(图4)获取class全称

(图5) 获取所有 classpath 下面的 META-INF/spring.factories文件的urls

(图6) 获取spring.factories文件的具体位置及文件中的内容)(图7) 获取spring.factories文件内容中的对应类列表

( 图8)

总结

通过classLoader加载classpath下面的META-INF/spring.factories文件,获取文件信息解析成配置属性存入结果集中,在根据type的全限定名称,从结果集中获取type对应的结果集。循环便利该结果集根据"全限定名称"创建实例。对实例集合排序后返回

关于如何理解springboot2.0.6中META-INF/spring.factories通过系统加载类获取对应的class的全限定名称就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0