zoukankan      html  css  js  c++  java
  • springboot源码解析(三)-从源码角度分析系统初始化器排序原理

    开篇之前先把祖师爷搬出来
      费玉清:问大家一个脑筋急转弯,500个女人在沙滩上裸奔,打一种体育运动项目,在大学生的春季或者秋季运动会上都会有这个项目
          思考。。。
          思考。。。
          思考。。。
      揭晓谜底:铅球
      反正谜底我已经揭晓了,至于大家能不能看到,我就不管了,哈哈
     

    概述

      前面两篇文章分别分析了系统初始化的作用,以及自定义系统初始化器的3种方法,而且还从源码的角度分析了系统初始化器的实现原理,这篇文章分析一下上一篇文章的一个遗留问题,系统初始化器是如何排序的。

    系统初始化器排序源码位置

    在SpringApplication的构造方法

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.sources = new LinkedHashSet();
            this.bannerMode = Mode.CONSOLE;
            this.logStartupInfo = true;
            this.addCommandLineProperties = true;
            this.addConversionService = true;
            this.headless = true;
            this.registerShutdownHook = true;
            this.additionalProfiles = new HashSet();
            this.isCustomEnvironment = false;
            this.lazyInitialization = false;
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
         //下面一行就是从spring-factories中加载系统初始化的代码,在上一篇文章有标注
         this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }

     进入this.getSpringFactoriesInstances

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
            return this.getSpringFactoriesInstances(type, new Class[0]);
        }
    
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = this.getClassLoader();
            Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        //这一行就是实现排序的代码
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

    我们进入sort方法

    public static void sort(List<?> list) {
            if (list.size() > 1) {
                list.sort(INSTANCE);
            }
    
        }

    为了把这一块彻底搞清楚,我还专门去看了这个sort排序的源码:java8中List中sort方法解析,感兴趣的小伙伴可以先看看这个,如果觉得写的太长了,这里只要明白一点就可以了,sort方法的参数需要传入一个实现了Comparator接口的实现类,主要需要实现这个接口的compare(T o1, T o2)方法,list排序比较大小就是根据这个方法实现的。

    我们进入INSTANCE的compare方法

    public int compare(@Nullable Object o1, @Nullable Object o2) {
            return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
        }

    我们继续进入doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);方法

    private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
            //这里的意思是说如果需要比较的对象实现了Priorityordered接口的话,就返回true,否则false
            boolean p1 = o1 instanceof PriorityOrdered;
            boolean p2 = o2 instanceof PriorityOrdered;
         //这里会发现如果实现了PriorityOrdered接口,比较的方法很简单,这里注意返回1,就是o1 > o2,否则相反
    if (p1 && !p2) { return -1; } else if (p2 && !p1) { return 1; } else { //下面这两个getOrder方法就是分别获取o1和o2对象的order的值 int i1 = this.getOrder(o1, sourceProvider); int i2 = this.getOrder(o2, sourceProvider); //后去到order值之后,直接调用Integer对象的比较器进行比较,因为Integer是实现了Comparator接口的 return Integer.compare(i1, i2); } }

    还记得在上一篇文章中说过,实现排序有两种方式,第一种是通过@Order注解,第二种是实现Order接口,一会看代码大家会发现有些系统初始化器即没有使用@Order注解,也没有实现Order接口,这时getOrder会返回一个非常大的值,意思就是说你排在最后。

    先看一下我的代码中有哪些系统初始化器

    下面我就举几个例子:

    排在第一的FirstApplicationContextInitializer是自定义的系统初始化器使用了@Order注解

    排在第二的ConfigurationWarningsApplicationContextInitializer,大家可以自己在idea中看一下,这个系统初始化器即没有实现Order接口,也没有使用@Order注解

    排在第四的DelegatingApplicationContextInitializer,这个系统初始化器在上一篇文章中讲过,实现了Order接口

     this.getOrder(o1, sourceProvider); 方法过程如下。

    private int getOrder(@Nullable Object obj, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
            Integer order = null;
            //由于上面传的sourcProvider为null这个if的就不分析了
            if (obj != null && sourceProvider != null) {
                Object orderSource = sourceProvider.getOrderSource(obj);
                if (orderSource != null) {
                    if (orderSource.getClass().isArray()) {
                        Object[] sources = ObjectUtils.toObjectArray(orderSource);
                        Object[] var6 = sources;
                        int var7 = sources.length;
    
                        for(int var8 = 0; var8 < var7; ++var8) {
                            Object source = var6[var8];
                            order = this.findOrder(source);
                            if (order != null) {
                                break;
                            }
                        }
                    } else {
                        order = this.findOrder(orderSource);
                    }
                }
            }
            //这里order=null,执行this.getOrder(obj)
            return order != null ? order : this.getOrder(obj);
        }

    进入this.getOrder(obj);

    protected int getOrder(@Nullable Object obj) {
            if (obj != null) {
           //这一步就是找order的 Integer order
    = this.findOrder(obj); if (order != null) { return order; } }      //如果obj即没有使用@Order注解,也没有实现Order接口,就返回这个 return 2147483647; }

    进入this.findOrder(obj); 大家注意这个this,是字类AnnotationAwareOrderComparator,而不是父类OrderComparator,我这里只贴出来方法,大家要结合自己的代码看

    @Nullable
    protected Integer findOrder(Object obj) {
       //<1.1> 这个就是调用父类OrderComparator中的findOrder,主要是看对象obj是否实现了Order接口,如果实现了,这里就会直接后去到值
    Integer order = super.findOrder(obj);
      //<1.2> 如果order为null,说明obj没有实现Order接口,那就从注解中找
    return order != null ? order : this.findOrderFromAnnotation(obj);
    }

    进入<1.1>super.findOrder(obj);

    @Nullable
        protected Integer findOrder(Object obj) {
         //这里就是判断obj有没有实现Ordered接口,如果实现了就直接调用getOrder方法
    return obj instanceof Ordered ? ((Ordered)obj).getOrder() : null; }

    进入((Ordered)obj).getOrder(),这里就是调用obj中getOrder()方法,我们假设这个obj就是DelegatingApplicationContextInitializer,我们进入里面看一下getOrder()方法

    public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
        
        private int order = 0;
        public int getOrder() {
                return this.order;
            }
       //这里省略了大部分方法 }

     可以发现,这里返回的order就是0.这就解释了上一节没有解释的一个问题,就是没有使用@Order注解的类如何使内部定义的order生效

    进入注释<1.2>,this.findOrderFromAnnotation(obj);

    @Nullable
        private Integer findOrderFromAnnotation(Object obj) {
         //一般的obj都不会实现AnnotatedElement,但是Class实现了这个接口,至于这个接口的作用大家自己百度 AnnotatedElement element
    = obj instanceof AnnotatedElement ? (AnnotatedElement)obj : obj.getClass();
         //获取注解 MergedAnnotations annotations
    = MergedAnnotations.from((AnnotatedElement)element, SearchStrategy.TYPE_HIERARCHY);
         //从注解中后去order注解,并获取其值 Integer order
    = OrderUtils.getOrderFromAnnotations((AnnotatedElement)element, annotations); return order == null && obj instanceof DecoratingProxy ? this.findOrderFromAnnotation(((DecoratingProxy)obj).getDecoratedClass()) : order; }

    看一下我程序中FirstApplicationContextInitializer的debug结果

    如果大家对Class类是怎么获取到注解的感兴趣可以看一下这篇文章:AnnotatedElement

    至此,基本已经分析完成获取order的过程,springboot启动过程的系统初始化器也就完全分析完了,本人也很小白,望大家多多指教,其实对于Java到底是怎么加载到注解的我之前也很困惑,今天才发现很多是通过Class类获取的注解,而且使用的是一个本地的native方法获取的,而且使用了Java中的一个魔法类Unsafe,这个还不太懂,以后还要学一下这个魔法类,感觉很厉害的样子。

    参考文章:

    子类调用父类方法中的this

  • 相关阅读:
    [洛谷P1155] 双栈排序
    [洛谷P4315] 月下”毛景“树
    [洛谷P2486] [SDOI2011]染色
    [HNOI2010] 弾飞绵羊
    mysql注入总结
    cisco交换机实现端口聚合
    python为运维人员打造一个监控脚本
    复习ACCESS注入
    利用sfc文件构建网络渗透
    FTP站点设置
  • 原文地址:https://www.cnblogs.com/gunduzi/p/13025433.html
Copyright © 2011-2022 走看看