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

原创
2019/12/30 20:01
阅读数 229


说明:本章在之前章节《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<SpringBootExceptionReporter> 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<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { //需要查找的接口名称字符串 String factoryClassName = factoryClass.getName(); //读取磁盘文件进行加载,返回找到封装结果的Map中key为需要查找的接口名称的values return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//先从缓存查找当前classloader是否加载过 MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { //如果之前加载过就返回,这里体验了读取文件资源实际做了缓存的 //后面都是从缓存读取 return result; }
try {
//从当前classloader的classpath下读取META-INF/spring.factories //封装返回Enumeration迭代器 Enumeration<URL> 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 <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> 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. * <p>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<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>(); ... 省略其它 }


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



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

Java软件编程之家


本文分享自微信公众号 - Java软件编程之家(gh_b3a87885f8f5)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部