• Spring核心接口Ordered的实现及应用 (动态切换数据源时候用到)


    Spring框架中有这个一个接口,名字叫Ordered,联想我们在数据库中应用的Ordered,很容易想到它的含义就是用来排序。那么问题来了,Spring中为什么要定义这样一个排序接口呢。我们知道spring框架使用了大量的策略设计模式。策略设计模式意味着我们的同一个接口,会有大量的不同实现。那么这么多实现,先执行哪个,后执行哪个呢。这就产生了一个排序和优先级的问题,于是Ordered接口登场,用来解决这一问题。

    ordered接口的正式介绍

    public interface Ordered {
      
        int HIGHEST_PRECEDENCE = -2147483648;
    
        int LOWEST_PRECEDENCE = 2147483647;
    
        int getOrder();
      
    }

    从上述代码中,我们可以看到ordered接口的实现是非常简单的。有一个最高的优先级和一个最低的优先级,还提供了一个获得当前实现类的order数值的方法。
    spring的order中。越小的值,优先级越高,越大的值优先级越低。

    一、ordered接口的应用
    介绍完ordered接口之后,我们来看一下实际的应用场景。
    有一个典型的场景,我们知道spring的事务管理是通过aop切面来实现的。当我们自己写aop实现的时候,与事务的切面同时切到了一段代码。那么spring应该先执行谁呢。举一个具体的例子,我们写了一个切换数据源的aspect切面。如果说事务的执行在数据源切换的前面,那么切换数据源就失败了。我们肯定希望先执行切换数据源,再执行事务。
    于是ordered的应用场景就来了。
    假设我们写一个下面的切面。

    @Component
    @Aspect
    public class ChangeDataBase implements Ordered {
        
        //拦截所有的service操作
        @Pointcut("execution( * com.color.*.service.*.*(..))")
        public void point() {
        }
     
        @Before("point()")
        public void onlyReadPre() {
            DataSourceContextHolder.setDataSourceType(DataSourceType.MYSQL);
            System.out.println("数据库切换MYSQL");
        }
     
        @After("point()")
        public void onlyReadPast() {
            DataSourceContextHolder.setDataSourceType(DataSourceType.ORACLE);
            System.out.println("数据库切换回ORACLE");
        }
     
        @Override
        public int getOrder() {
            return 1;
        }
    }

    在上述代码中,我们定义了一个切点,用于拦截所有的service的方法。然后再方法执行前,我们将数据库切换到mysql,方法执行之后,数据库切换成oracle。
    最后重写了ordered接口的getOrder方法。这里我们设置order的级别为1。
    如果是使用注入bean的方式的话,直接实现接口和上方一样使用即可。
    这个时候,我们就会发现。切换数据源的方法会永远在事务之前执行,这就实现了我们的目的。

    二、order注解的使用
    读到现在的读者在想,还要实现接口感觉好麻烦啊,有没有什么更方便的方法呢。当然有,我们介绍一下@Order注解。
    还是先看一下order注解的源码。

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    @Documented
    public @interface Order {
        int value() default 2147483647;
    }

    默认的orver值是最大的,也就是优先级是最低的。
    我们在使用的时候,只要在类上面打上order注解即可。
    我们模拟两个类,打上order注解,然后再spring容器启动的时候,对类进行空参构造函数加载,通过空参构造函数里面的打印情况,我们就可以看到类初始化和执行的顺序。
    建立我们的第一个order类。

    @Component 
     
    //使用order属性,设置该类在spring容器中的加载顺序
    @Order(1)  
    public class Order1 {
         
        private final int ORDERED = 1;
         
        public Order1(){
            System.out.println(this);
        }
      
        @Override
        public String toString() {
            return "Order1 is  loaded @ORDERED=" + ORDERED + "]";
        }
     
    }

    建立我们的第二个order类。

    @Component 
     
    //使用order属性,设置该类在spring容器中的加载顺序
    @Order(2)  
    public class Order2 {
         
        private final int ORDERED = 2;
         
        public Order2(){
            System.out.println(this);
        }
      
        @Override
        public String toString() {
            return "Order2 is  loaded @ORDERED=" + ORDERED + "]";
        }
     
    }

    启动spring容器之后,我们看到控制台执行如下结果。

    Order1 is  loaded @ORDERED=1]
    Order2 is  loaded @ORDERED=2]

    三、orderComparator的介绍
    那么我们假如想知道一个类的order的值,或者想比较两个类的order值谁大谁小,这个时候要如何操作呢,Spring贴心的给我们提供了一个类。OrderComparator,通过这个类,我们获得实例后,使用它所提供的getOrder或者compare方法即可实现上述的需求。
    我们照例还是先来看一下源码。

    public class OrderComparator implements Comparator<Object> {
        public static final OrderComparator INSTANCE = new OrderComparator();
    
        public OrderComparator() {
        }
    
        public Comparator<Object> withSourceProvider(OrderComparator.OrderSourceProvider sourceProvider) {
            return (o1, o2) -> {
                return this.doCompare(o1, o2, sourceProvider);
            };
        }
    
        public int compare(@Nullable Object o1, @Nullable Object o2) {
            return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
        }
    
        private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
            boolean p1 = o1 instanceof PriorityOrdered;
            boolean p2 = o2 instanceof PriorityOrdered;
            if (p1 && !p2) {
                return -1;
            } else if (p2 && !p1) {
                return 1;
            } else {
                int i1 = this.getOrder(o1, sourceProvider);
                int i2 = this.getOrder(o2, sourceProvider);
                return Integer.compare(i1, i2);
            }
        }
    
        private int getOrder(@Nullable Object obj, @Nullable OrderComparator.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);
                        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);
                    }
                }
            }
    
            return order != null ? order.intValue() : this.getOrder(obj);
        }
    
        protected int getOrder(@Nullable Object obj) {
            if (obj != null) {
                Integer order = this.findOrder(obj);
                if (order != null) {
                    return order.intValue();
                }
            }
    
            return 2147483647;
        }
    
        @Nullable
        protected Integer findOrder(Object obj) {
            return obj instanceof Ordered ? ((Ordered)obj).getOrder() : null;
        }
    
        @Nullable
        public Integer getPriority(Object obj) {
            return null;
        }
    
        public static void sort(List<?> list) {
            if (list.size() > 1) {
                list.sort(INSTANCE);
            }
    
        }
    
        public static void sort(Object[] array) {
            if (array.length > 1) {
                Arrays.sort(array, INSTANCE);
            }
    
        }
    
        public static void sortIfNecessary(Object value) {
            if (value instanceof Object[]) {
                sort((Object[])((Object[])value));
            } else if (value instanceof List) {
                sort((List)value);
            }
    
        }
    
        @FunctionalInterface
        public interface OrderSourceProvider {
            @Nullable
            Object getOrderSource(Object var1);
        }
    }

    我们先来重点看一下doCompare方法。判断逻辑如下:
    若对象o1是Ordered接口类型,o2是PriorityOrdered接口类型,那么o2的优先级高于o1
    若对象o1是PriorityOrdered接口类型,o2是Ordered接口类型,那么o1的优先级高于o2
    其他情况,若两者都是Ordered接口类型或两者都是PriorityOrdered接口类型,调用Ordered接口的getOrder方法得到order值,order值越大,优先级越小
    那么一句话来说就是这样的。
    OrderComparator比较器进行排序的时候,若2个对象中有一个对象实现了PriorityOrdered接口,那么这个对象的优先级更高。
    若2个对象都是PriorityOrdered或Ordered接口的实现类,那么比较Ordered接口的getOrder方法得到order值,值越低,优先级越高。

    再来看一下getOrder方法。
    传入一个对象后,通过provider取得原始对象。如果不为空,继续进行判断。
    如果是数组对象,对对象进行遍历,得到order后,跳出。如果不是数组则直接获得对象的order。
    最后如果order如果不是空,直接返回order的int值,为空的时候,通过findOrder查看,返回的是order的最大值,也就是最低优先级。

    早年同窗始相知,三载瞬逝情却萌。年少不知愁滋味,犹读红豆生南国。别离方知相思苦,心田红豆根以生。
  • 相关阅读:
    相交链表 3种方法
    旋转数组 空间复杂度为O(1) 的2 种方法 + 1种空间复杂度O(n)
    各种sort排序总结
    我写过的bug...
    裸BFS题若干
    luence
    mysql 找出外键等约束
    mysql (8.0 或以下)数据 卸载, 安装, 创建用户, 赋权
    navicat 导入sql文件 的正确方法
    [转] git clone 远程分支
  • 原文地址:https://www.cnblogs.com/shanheyongmu/p/15433976.html
Copyright © 2020-2023  润新知