springboot源码解析-管中窥豹系列之排序(五)
一、前言
- Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去。
- 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot源码管中窥豹系列。
二、排序
- 前几节我们讲源码的时候,会遇到一些排序的问题,我们都避而不谈
比如获取initializer时的排序:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
比如对runner排序:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// (1) 找到ApplicationRunner的实现类,加到list里面
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// (2) 找到CommandLineRunner的实现类,加到list里面
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// (3) 排序
AnnotationAwareOrderComparator.sort(runners);
// (4) 钩子回调
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
我们来分析一下这个排序AnnotationAwareOrderComparator.sort(list)的源码。
三、源码解析
public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
public static void sort(List<?> list) {
if (list.size() > 1) {
list.sort(INSTANCE);
}
}
- 已new的AnnotationAwareOrderComparator对象作为参数,排序
- list.sort(comparator)是jdk自带的排序, 通用的
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
- 我们知道排序要么实现Comparable接口,要么new Comparator, spring用的第二种
- 我们重点看下这个comparator : AnnotationAwareOrderComparator
public class AnnotationAwareOrderComparator extends OrderComparator {
...
}
public class OrderComparator implements Comparator<Object> {
...
}
compare方法在OrderComparator里面:
@Override
public int compare(@Nullable Object o1, @Nullable Object o2) {
return doCompare(o1, o2, null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
if (p1 && !p2) {
return -1;
}
else if (p2 && !p1) {
return 1;
}
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
- 先判断有没有实现PriorityOrdered接口,实现了的比没实现的有高优先级
- 再用getOrder()判断
private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
Integer order = null;
if (obj != null && sourceProvider != null) {
Object orderSource = sourceProvider.getOrderSource(obj);
if (orderSource != null) {
if (orderSource.getClass().isArray()) {
Object[] sources = ObjectUtils.toObjectArray(orderSource);
for (Object source : sources) {
order = findOrder(source);
if (order != null) {
break;
}
}
}
else {
order = findOrder(orderSource);
}
}
}
return (order != null ? order : getOrder(obj));
}
sourceProvider为空,我们可以直接看最后一行。
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
protected int getOrder(@Nullable Object obj) {
if (obj != null) {
Integer order = findOrder(obj);
if (order != null) {
return order;
}
}
return Ordered.LOWEST_PRECEDENCE;
}
protected Integer findOrder(Object obj) {
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
}
- 判断有没有实现Ordered接口,如果有就取出来它的order值
- 如果没有,就取Integer.MAX_VALUE
- 注意,order值越小,优先级越高
- 注意,上面findOrder方法是protected, 我们最开始说的AnnotationAwareOrderComparator对它进行了重写
@Override
@Nullable
protected Integer findOrder(Object obj) {
Integer order = super.findOrder(obj);
if (order != null) {
return order;
}
return findOrderFromAnnotation(obj);
}
@Nullable
private Integer findOrderFromAnnotation(Object obj) {
AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
if (order == null && obj instanceof DecoratingProxy) {
return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
}
return order;
}
- 先调用父类的findOrder方法
- 没找到,再调用findOrderFromAnnotation方法
- 最重要的是这一行:OrderUtils.getOrderFromAnnotations(element, annotations),我们进去看看
@Nullable
static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) {
if (!(element instanceof Class)) {
return findOrder(annotations);
}
Object cached = orderCache.get(element);
if (cached != null) {
return (cached instanceof Integer ? (Integer) cached : null);
}
Integer result = findOrder(annotations);
orderCache.put(element, result != null ? result : NOT_ANNOTATED);
return result;
}
@Nullable
private static Integer findOrder(MergedAnnotations annotations) {
MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
if (orderAnnotation.isPresent()) {
return orderAnnotation.getInt(MergedAnnotation.VALUE);
}
MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
if (priorityAnnotation.isPresent()) {
return priorityAnnotation.getInt(MergedAnnotation.VALUE);
}
return null;
}
private static final String JAVAX_PRIORITY_ANNOTATION = "javax.annotation.Priority";
- 两个方法,第一个是简单的缓存,不用看,重点看第二个方法
- 先判断有没有org.springframework.core.annotation.Order注解
- 再判断有没有javax.annotation.Priority注解
- 有注解就去上面的值,没有返回null
回到最开始的方法,进行int值的排序
Integer.compare(i1, i2);
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
至此,我们的排序源码就分析完了,我们总结一下:
- (1)先判断有没有实现PriorityOrdered,实现PriorityOrdered比没实现的有高优先级
- (2)第一步如果比较不出来,判断有没有实现Ordered,如果实现了,取实现方法的int值比较
- (3)如果没有实现Ordered,判断有没有org.springframework.core.annotation.Order注解, 有注解,取注解上的order值,进行比较
- (4)如果没有Order注解,判断有没有javax.annotation.Priority注解,取注解上的值比较
- (5)如果上面都没有,返回Integer.MAX_VALUE,值越大优先级越低
欢迎关注微信公众号:丰极,更多技术学习分享。
springboot源码解析-管中窥豹系列之排序(五)的更多相关文章
- springboot源码解析-管中窥豹系列
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之自动装配(九)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之BeanDefine如何加载(十三)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之BeanPostProcessor(十二)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之bean如何生成?(十四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之BeanFactoryPostProcessor(十一)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之web服务器(七)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之BeanDefinition(八)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之EnableXXX(十)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
随机推荐
- 微软面试题:剑指 Offer 51. 数组中的逆序对 Hard 出现次数:3
题目描述: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对. 输入一个数组,求出这个数组中的逆序对的总数. 示例 1: 输入: [7,5,6,4] 输出: 5 限制: ...
- Java集合源码分析(九)——HashSet
简介 HashSet就是一个集合,里面不能有重复的元素,元素也是无序的. HashSet其实就是调用了HashMap实现的,所以,它也不是线程安全的. HashSet通过iterator()返回的迭代 ...
- 《深入理解计算机系统》实验三 —— Buf Lab
这是CSAPP的第三个实验,主要让我们熟悉GDB的使用,理解程序栈帧的结构和缓冲区溢出的原理. 实验目的 本实验的目的在于加深对IA-32函数调用规则和栈结构的具体理解.实验的主要内容是对一个可执 ...
- 网络 IO 模型简单介绍
一.同步阻塞 IO(BIO) 当用户线程调用了 read 系统调用,内核(kernel)就开始了 IO 的第一个阶段:准备数据.很多时候,数据在一开始还没有到达(比如,还没有收到一个完整的Socket ...
- TLS 协议
一.背景 参与了一个Sofa-RPC开源项目,认领了TLS的任务,记录下这次宝贵的经历,并感谢章哥的信任以及在整个过程中对我的帮助. 负责的部分不难,主要是使用h2(HTTP 2加密)协议,完成RPC ...
- java-01-基础语法
1. 注释 单行注释 格式: // 注释信息 多行注释 格式: /* 注释信息 */ 文档注释 格式: /** 注释信息 */ 2. 常量 定义:在程序运行过程中,其值不可发生改变的量 分类: 常量类 ...
- 详解Java中的IO输入输出流!
目录 本片要点 基本分类 发展史 文件字符流 输出的基本结构 流中的异常处理 异常处理新方式 读取的基本结构 运用输入与输出完成复制效果 文件字节流 缓冲流 字符缓冲流 装饰设计模式 转换流(适配器) ...
- create-react-app 基于TS的项目
写在前面 最近在用React,发现百度了很多都没有找到基于TS的React项目搭建,很多是老的方法已经属于不成功的了,今天我把最新的搭建基于ts的React的项目分享出来 create-react-a ...
- [日常摸鱼]Vijos1083小白逛公园-线段树
题意:单点修改,询问区间最大子段和,$n\leq 5e5$ 考虑分治的方法$O(nlogn)$求一次最大子段和的做法,我们是根据中点分成左右两个区间,那么整个区间的答案要么是左边答案,要么是右边答案, ...
- MyBatisPlus-常用注解
一.@TableName 映射数据库的表名 package com.md.entity; import com.baomidou.mybatisplus.annotation.*; import co ...