一、前言

  • 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源码解析-管中窥豹系列之排序(五)的更多相关文章

  1. SpringBoot源码解析系列文章汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的SpringBoot源码解析系列文章的汇总,当你使用SpringBoot不仅仅满足于基本使用时.或者出去面试被面试官虐了时.或者说想要深入了解一下 ...

  2. 【spring-boot 源码解析】spring-boot 依赖管理梳理图

    在文章 [spring-boot 源码解析]spring-boot 依赖管理 中,我梳理了 spring-boot-build.spring-boot-parent.spring-boot-depen ...

  3. SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis

    本篇我们在SpringBoot中整合Mybatis这个orm框架,毕竟分析一下其自动配置的源码,我们先来回顾一下以前Spring中是如何整合Mybatis的,大家可以看看我这篇文章Mybaits 源码 ...

  4. React源码解析之React.Children.map()(五)

    一,React.Children是什么? 是为了处理this.props.children(this.props.children表示所有组件的子节点)这个属性提供的工具,是顶层的api之一 二,Re ...

  5. SpringBoot 源码解析 (二)----- Spring Boot精髓:启动流程源码分析

    本文从源代码的角度来看看Spring Boot的启动过程到底是怎么样的,为何以往纷繁复杂的配置到如今可以这么简便. 入口类 @SpringBootApplication public class He ...

  6. SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析

    在上一篇博客中分析了springBoot启动流程,大体的轮廓只是冰山一角.今天就来看一下springBoot的亮点功能:自动化装配功能. 先从@SpringBootApplication开始.在启动流 ...

  7. SpringBoot 源码解析 (八)----- Spring Boot 精髓:事务源码解析

    本篇来讲一下SpringBoot是怎么自动开启事务的,我们先来回顾一下以前SSM中是如何使用事务的 SSM使用事务 导入JDBC依赖包 众所周知,凡是需要跟数据库打交道的,基本上都要添加jdbc的依赖 ...

  8. SpringBoot 源码解析 (十)----- Spring Boot的核心能力 - 集成AOP

    本篇主要集成Sping一个重要功能AOP 我们还是先回顾一下以前Spring中是如何使用AOP的,大家可以看看我这篇文章spring5 源码深度解析----- AOP的使用及AOP自定义标签 Spri ...

  9. TreeSet集合的add()方法源码解析(01.Integer自然排序)

    >TreeSet集合使用实例 >TreeSet集合的红黑树 存储与取出(图) >TreeSet的add()方法源码     TreeSet集合使用实例 package cn.itca ...

  10. 【spring-boot 源码解析】spring-boot 依赖管理

    关键词:spring-boot 依赖管理.spring-boot-dependencies.spring-boot-parent 问题 maven 工程,依赖管理是非常基本又非常重要的功能,现在的工程 ...

随机推荐

  1. sharepoint定义固定的网站集

    SPSite site = new SPSite(http://192.168.0.3/);            SPWeb web = site.RootWeb;

  2. 解决eclipse中egit中的cannot open git-upload-pack问题

    一.背景 今天在使用eclipse的egit插件进行检出远程代码到本地时,出现了cannot open git-upload-pack错误,后经过努力解决该问题,记录下方便回顾和交流! 二.出现原因 ...

  3. WPF捕捉Windows关机事件

    private const int SC_SCREENSAVE = 0xF140; private const int WM_QUERYENDSESSION = 0x0011; private boo ...

  4. 用Delphi“遥控”按钮

    很多情况下,我们需要在程序中实现这样的功能:在自编写的程序里控制另外一软件中的某个按钮被按下.比如,有一天你在聊QQ时觉得烦了,那么就想写程序来帮你按下“发送”按钮,省得你自己一次次动手了.那么,这个 ...

  5. JS如何调用隐藏按钮的click事件

    js如何调用隐藏按钮的click事件:1.设定隐藏不要使用Visiable属性,使用style.display=none:2.触发JS事件可以使用fireEvent方法,如:document.getE ...

  6. CF 217 B. Berland Bingo

    http://codeforces.com/contest/370/problem/B 题意 :呃,这个题我说不清楚....就是有n个人,第 i 个人手里有 mi 张牌,如果,现在主人念数,念到哪张牌 ...

  7. Oracle OCI-22053:溢出错误

    Oracle 数值数据类型最多可存储 38 个字节的精度.当将 Oracle 数值转换 为公共语言运行库数据类型时,小数点后边的位数可能过多,这会导致此错误.   查询29万笔数据,报此错误,分析应该 ...

  8. matlab 对图像操作的函数概览

    转自博客:http://blog.163.com/fei_lai_feng/blog/static/9289962200991713415422/ 一. 读写图像文件 1. imread imread ...

  9. AIX下解决POWERHA的脑裂问题

    一.安装创建并发vg时必需的软件包clvm包,该包安装.升级.后必须重启os clvm包的描述:Enhanced Concurrent Logical Volume Manager 软件包在aix61 ...

  10. MyEclipse出现红色感叹号解决办法

    今天在做数据库连接练习的时候自己创建的工程突然出现了一个红色的感叹号,然后运行自己写的代码的时候出现找不到主类的错误!!! 我还以为是自己不小心写错了,然后让编译器自动生成代码也出现了一样的问题... ...