微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

一文带你了解Spring核心接口Ordered的实现及应用

前言

最近在看框架的时候,发现了这个接口,在此进行总结,希望能够给大家帮助,同时提升自己。

order接口的大体介绍

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

ordered接口的正式介绍

首先我们通过spring的源码看一下Ordered接口,源码如下:

@H_404_12@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的应用场景就来了。
假设我们写一个下面的切面。

@H_404_12@@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。
这个时候,我们在配置事务切面的时候。在xml中配置order。

@H_404_12@<tx:annotation-driven transaction-manager="transactionManager" order="2"/>

如果是使用注入bean的方式的话,直接实现接口和上方一样使用即可。
这个时候,我们就会发现。切换数据源的方法会永远在事务之前执行,这就实现了我们的目的。

order注解的使用

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

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

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

@H_404_12@@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类。

@H_404_12@@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容器之后,我们看到控制台执行如下结果。

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

orderComparator的介绍

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

@H_404_12@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,(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的最大值,也就是最低优先级。

@H_404_12@protected int getorder(@Nullable Object obj) { if (obj != null) { Integer order = this.findOrder(obj); if (order != null) { return order.intValue(); } } return 2147483647; }

总结

至此 ordered相关的东西就介绍到此为止,文中难免有不足,希望大家提出指正,感谢。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐