SpringBoot到Spring源码分析之META-INF/spring.factories解析过程

SpringBoot到Spring源码分析之META-INF/spring.factories解析过程_第1张图片

说明:本章在之前章节《SpringBoot 启动流程源码分析》的基础上进行继续源码分析。

 

前面我们分析到SpringApplication类的run方法,这个方法主要在顶层设计上定义了SpringBoot项目的整个启动过程,同时包括了Spring容器的启动过程。本章继前面的基础上继续分析META-INF/spring.factories文件的加载过程,META-INF/spring.factories文件是springboot 框架识别并解析starter的核心文件,了解springboot加载META-INF/spring.factories文件原理至关重要。

SpringApplication类的run方法源码:

public ConfigurableApplicationContext run(String... args) {


   //创建程序计时器
   StopWatch stopWatch = new StopWatch();
   //启动程序计时器
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection exceptionReporters = new ArrayList<>();
   //设置java.awt.headless系统属性值
   configureHeadlessProperty();
   //从缓存的META-INF/spring.factories Map中获取
   //SpringApplicationRunListeners接口的所有子类
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //回调通知应用开始启动事件监听器
   listeners.starting();
   
   ...省略其它
}


程序计数器new StopWatch()源码分析

public StopWatch() {
   this("");
}


public StopWatch(String id) {
   this.id = id;
}


public void start() throws IllegalStateException {
   start("");
}


public void start(String taskName) throws IllegalStateException {
   if (this.currentTaskName != null) {
      throw new IllegalStateException("Can't start StopWatch: it's already running");
   }
   this.currentTaskName = taskName;
   this.startTimeNanos = System.nanoTime();
}


java.awt.headless属性配置源码分析:

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;


private void configureHeadlessProperty() {
   System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
         System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

getRunListeners(args)源码分析,这是本章重点内容:

private SpringApplicationRunListeners getRunListeners(String[] args) {


   //SpringApplicationRunListener实现类构造器参数类型数组
   //默认实现类为EventPublishingRunListener
   Class[] types = new Class[]{SpringApplication.class, String[].class};


   //将SpringApplicationRunListener实现类封装到SpringApplicationRunListeners
   //SpringApplicationRunListeners支持通过for所有事件list批量执行功能
//后面分析starting方法时可以看到for代码
   return new SpringApplicationRunListeners(logger,
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}


public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
   
   //需要查找的接口名称字符串
   String factoryClassName = factoryClass.getName();
   
   //读取磁盘文件进行加载,返回找到封装结果的Map中key为需要查找的接口名称的values
   return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}


private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {


   //先从缓存查找当前classloader是否加载过
   MultiValueMap result = cache.get(classLoader);
   if (result != null) {
      //如果之前加载过就返回,这里体验了读取文件资源实际做了缓存的
      //后面都是从缓存读取
      return result;
   }


   try {


      //从当前classloader的classpath下读取META-INF/spring.factories
      //封装返回Enumeration迭代器
      Enumeration urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();


      //迭代所有META-INF/spring.factories文件
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         //将META-INF/spring.factories文件转为UrlResource对象
         UrlResource resource = new UrlResource(url);
         //将META-INF/spring.factories文件从UrlResource对象转为Properties映射表
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         //遍历META-INF/spring.factories文件entry
         for (Map.Entry entry : properties.entrySet()) {
            String factoryClassName = ((String) entry.getKey()).trim();
            
            //遍历value逗号分隔返回的String[]
            for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {


               //放到LinkedMultiValueMap,key为接口全名称字符串,
               //value为接口实现类的全名称字符串
               result.add(factoryClassName, factoryName.trim());
            }
         }
      }
      
      //保存到ConcurrentReferenceHashMap,key为当前classloader
      //value为所有META-INF/spring.factories文件k,v Map
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}


private  List createSpringFactoriesInstances(Class type, Class[] parameterTypes,
                                       ClassLoader classLoader, Object[] args, Set names) {
   
   List instances = new ArrayList<>(names.size());
   for (String name : names) {
      try {
         //获取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;
}

总结

  本文基于前面源码分析文章继续解读了springboot读取META-INF/spring.factories过程。通过classloaderclasspath下读取所有的META-INF/spring.factories文件,然后通过Map键值对数据结构保存在spring core模块下的SpringFactoriesLoader抽象类的静态属性cache中。

public abstract class SpringFactoriesLoader {


  /**
   * The location to look for factories.
   * 

Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); private static final Map> cache = new ConcurrentReferenceHashMap<>(); ... 省略其它  }

后续所有需要从META-INF/spring.factories文件获取都是尝试先从这个缓存的Map中获取。至于从META-INF/spring.factories文件获取到各种不同接口的作用留到我们后面继续以分解式的方式独立讲解。

欢迎扫码下面二维码关注我

SpringBoot到Spring源码分析之META-INF/spring.factories解析过程_第2张图片

Java软件编程之家

你可能感兴趣的:(SpringBoot到Spring源码分析之META-INF/spring.factories解析过程)