c语言sscanf函数的用法是什么
315
2022-08-23
《SpringBoot启动流程一》:万字debug梳理SpringBoot如何加载并处理META-INF/spring.factories文件中的信息
一、前言
看Spring Boot源码的时候,发现在SpringApplication初始化阶段会加载Spring应用上下文初始化器(ApplicationContextInitializer)、加载Spring应用事件监听器(ApplicationListener);而ApplicationContextInitializer 和 ApplicationListener内建的实现类预置在spring-boot jar包的META-INF/spring.factories文件中;
此外,在spring-boot-autoconfigure jar包的META-INF/spring.factories文件中也有一部分:
所以,在Spring Boot中一共内建了11个ApplicationListener、7个ApplicationContextInitializer。
那么SpringBoot是怎么将其加载到Spring容器中的呢?怎么加载到SpringApplication中的呢?我们就此展开研究。
二、正文
入口
无论是在SpringApplication初始化阶段时加载Spring事件监听器ApplicationListener、Spring应用上下文初始化器ApplicationContextInitializer,还是在SpringApplication准备阶段时加载Spring运行时监听器SpringApplicationRunListener、异常报告器SpringBootExceptionReporter,都要从SpringApplication#getSpringFactoriesInstances()重载方法开始,并且进入到getSpringFactoriesInstances(Class
以Spring应用上下文初始化器为例,此处的type为ApplicationContextInitializer;整体的处理流程为:
下面我们分开来看;
1、找到type的所有实现类
使用Spring工厂加载机制方法SpringFactoriesLoader.loadFactoryName(Class,ClassLoader)来做这个操作;
SpringFactoriesLoader.loadFactoryName(Class,ClassLoader)中首先会根据类加载器加载出所有spring.factories中的所有内容。
1)loadSpringFactories(ClassLoader)
loadSpringFactories(ClassLoader)会解析所有加载的jar包中 META-INF/spring.factories配置文件的配置内容,并组装为Map
首先,去缓冲中查询是否有入参classLoader对应的配置信息(仅第一次加载spring.factories文件时不走缓存),如果存在,则表明服务之前解析过配置文件 并 方法返回。如果不存在,则进行解析操作。其次,获得所有依赖jar包中,具有META-INF/spring.factories配置文件的jar文件URI,并依次进行遍历。接着,将spring.factories配置的内容转化成properties实例;遍历properties实例,将key和value维护到Map
<1> 缓存cache数据结构:
static final Map
<2> 方法主体:
private static Map
在遍历properties实例,将key和value维护到Map
假设,我依赖的某一个jar包的META-INF/factories中和spring-boot jar包的META-INF/factories中都有 org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\。则ConfigurationWarningsApplicationContextInitializer会被添加两次到ApplicationContextInitializer对应的List
spring不会有这种bug吧?显然是不可能的,往上追,追到SpringApplication类中,在获取到type类所有实现类的类名时会用Set集合做一个去重。
思考一下为什么不能在最底层就做去重呢?而需要每个调用方都自己去重!
2)classLoader.getResources(FACTORIES_RESOURCE_LOCATION)
我很好奇,classLoader.getResources(FACTORIES_RESOURCE_LOCATION)它是如何找到所有META-INF/spring.factories文件的?所以这里特意写一小节。
此处的classLoader为AppClassLoader;比较有意思的是getResources(FACTORIES_RESOURCE_LOCATION)方法使用F7进不去,要进去到AppClassLoader里打断点。
AppClassLoader是Launcher的静态内部类,其类图如下:
即,AppClassLoader间接继承自ClassLoader,而getResources(String name)方法在其类结构中只出现在ClassLoader中,所以要去ClassLoader中打断点;
加载Resource资源时也会用到父类加载器。递归由AppClassLoader 的父加载器 ExtClassLoader 负责加载Resource资源;最终体现为:Enumeration
最后看一下AppClassLoader是怎么找到所有的META-INF/spring.factories文件的?
1> 因为ClassLoader#findResources(String)是一个抽象方法,具体逻辑由子类实现,结合AppClassLoader的类图,定位到URLClassLoader#findResources(String);
在URLClassLoader内部会调用其组合的URLClassPath类的findResources(String, boolean)方法去做一个真正的资源扫描操作。
最终效果如下,但是不建议追(太深了,并且很不好debug)。
但是有一点我们可以记住:hasMoreElements() 和 nextElement()方法均出自sun.misc包下的CompoundEnumeration类。有兴趣的建议参考博主打断点的思路继续深追如下代码段:
3)loadSpringFactories(ClassLoader)返回结果
接着通过Map的getOrDefault()方法获取到result中key为ApplicationContextInitializer的value。
最后回到SpringApplication#getSpringFactoriesInstances()方法中,使用Set集合来接返回值,以达到一个去重的效果。
2、实例化type的所有实现类
紧接着上面进入到createSpringFactoriesInstances()方法根据类的全路径名做实例化操作。
List
遍历所有的全路径类名,使用AppClassLoader将相应Class文件从磁盘装载到内存中,然后利用反射获取Class类的无参构造函数、实例化对象。
注意:要实例化的类必须要有无参构造函数。
private
3、做Order排序
将type类对应的所有实现类实例化完毕之后,要对他们做一个根据Order的排序。
AnnotationAwareOrderComparator.sort(instances);
sort()方法中直接使用List集合的sort()方法,但需要自定义Comparator为当前类实例AnnotationAwareOrderComparator。
再看AnnotationAwareOrderComparator的类结构:
AnnotationAwareOrderComparator继承自OrderComparator,自定义Comparator需要实现Comparator的抽象方法compare(T o1, T o2)。AnnotationAwareOrderComparator自身没有compare()方法,所以看其父类OrderComparator中的compare方法;
因为OrderComparator#doCompare(Object o1, Object o2, OrderSourceProvider sourceProvider)方法中的入参sourceProvider为null,所以进入到getOrder()方法时,后续直接调用子类的findOrder(Object obj)方法去查找相应类的顺序值。
AnnotationAwareOrderComparator#findOrder()方法中先调用父类OrderComparator#findOrder()方法,如果找到顺序值直接返回,否者从类的@Order注解中取到顺序值。
1> 先看OrderComparator#findOrder()方法:
该方法判断obj有没有实现Ordered接口,实现Ordered接口之后,有没有重写其getOrder()方法,如果重写了,则直接从getOrder()中获取到序列值。
@Nullableprotected Integer findOrder(Object obj) { return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);}
以ContextIdApplicationContextInitializer为例,其getOrder()方法返回的序列值为2147483637;
2> 再看AnnotationAwareOrderComparator#findOrder()方法:
如果通过OrderComparator#findOrder()获取不到序列值,则通过findOrderFromAnnotation()方法从@Order注解中获取序列值。
@Override@Nullableprotected Integer findOrder(Object obj) { Integer order = super.findOrder(obj); if (order != null) { return order; } return findOrderFromAnnotation(obj);}
以ConfigurationWarningsApplicationContextInitializer为例:
findOrderFromAnnotation()方法中通过OrderUtils.getOrderFromAnnotations(element, annotations);获取@Order注解中的值:
OrderUtils.getOrderFromAnnotations(element, annotations);方法返回之后,如果order为null,则直接返回null。
最后返回到OrderComparator#getOrder()方法,如果order为null,则将Order设置为Integer.MAX_VALUE;
如果两个对象的Order序列值一样,则按原本在集合中的顺序先后排列。
1)总述
利用List集合自身的排序,通过传入自定义的Comparator 实现排序规则。规则具体如下:
AnnotationAwareOrderComparator类继承自OrderComparator,排序规则体现在OrderComparator类中的compare()方法;每个排序对象都会通过OrderComparator#getOrder()方法获取排列的序列值。getOrder()方法中首先通过findOrder()方法查找序列值,而AnnotationAwareOrderComparator重写了findOrder()方法。所以调用findOrder()方法会先进入到AnnotationAwareOrderComparator#findOrder()方法。在AnnotationAwareOrderComparator#findOrder()方法中,会先调用其父类OrderComparator#findOrder()方法判断对象是否实现Ordered接口 并 重写了getOrder()方法,有则返回getOrder()的值。否则通过findOrderFromAnnotation()方法从对象的@Order注解中获取具体的值,如果对象没有被@Order注解标注,则返回null。最后到OrderComparator#getOrder()层面,如果findOrder()返回了具体的Integer值,则返回,否者返回Integer.MAX_VALUE。
下篇接着聊SpringBoot启动流程之SpringApplication准备阶段。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~