前言

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

order接口的大体介绍

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

ordered接口的正式介绍

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

  1. public interface Ordered {
  2. int HIGHEST_PRECEDENCE = -2147483648;
  3. int LOWEST_PRECEDENCE = 2147483647;
  4. int getOrder();
  5. }

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

spring的order中。越小的值,优先级越高,越大的值优先级越低。

ordered接口的应用

介绍完ordered接口之后,我们来看一下实际的应用场景。

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

于是ordered的应用场景就来了。

假设我们写一个下面的切面。

  1. @Component
  2. @Aspect
  3. public class ChangeDataBase implements Ordered {
  4. //拦截所有的service操作
  5. @Pointcut("execution( * com.color.*.service.*.*(..))")
  6. public void point() {
  7. }
  8. @Before("point()")
  9. public void onlyReadPre() {
  10. DataSourceContextHolder.setDataSourceType(DataSourceType.MYSQL);
  11. System.out.println("数据库切换MYSQL");
  12. }
  13. @After("point()")
  14. public void onlyReadPast() {
  15. DataSourceContextHolder.setDataSourceType(DataSourceType.ORACLE);
  16. System.out.println("数据库切换回ORACLE");
  17. }
  18. @Override
  19. public int getOrder() {
  20. return 1;
  21. }
  22. }

在上述代码中,我们定义了一个切点,用于拦截所有的service的方法。然后再方法执行前,我们将数据库切换到mysql,方法执行之后,数据库切换成oracle。

最后重写了ordered接口的getOrder方法。这里我们设置order的级别为1。

这个时候,我们在配置事务切面的时候。在xml中配置order。

  1. <tx:annotation-driven transaction-manager="transactionManager" order="2"/>

如果是使用注入bean的方式的话,直接实现接口和上方一样使用即可。

这个时候,我们就会发现。切换数据源的方法会永远在事务之前执行,这就实现了我们的目的。

order注解的使用

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

还是先看一下order注解的源码。

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

默认的优先级是最小的。

我们在使用的时候,只要在类上面打上order注解即可。

我们模拟两个类,打上order注解,然后再spring容器启动的时候,对类进行空参构造函数加载,通过空参构造函数里面的打印情况,我们就可以看到类初始化和执行的顺序。

建立我们的第一个order类。

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

建立我们的第二个order类。

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

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

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

orderComparator的介绍

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

我们照例还是先来看一下源码。

  1. public class OrderComparator implements Comparator<Object> {
  2. public static final OrderComparator INSTANCE = new OrderComparator();
  3. public OrderComparator() {
  4. }
  5. public Comparator<Object> withSourceProvider(OrderComparator.OrderSourceProvider sourceProvider) {
  6. return (o1, o2) -> {
  7. return this.doCompare(o1, o2, sourceProvider);
  8. };
  9. }
  10. public int compare(@Nullable Object o1, @Nullable Object o2) {
  11. return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
  12. }
  13. private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
  14. boolean p1 = o1 instanceof PriorityOrdered;
  15. boolean p2 = o2 instanceof PriorityOrdered;
  16. if (p1 && !p2) {
  17. return -1;
  18. } else if (p2 && !p1) {
  19. return 1;
  20. } else {
  21. int i1 = this.getOrder(o1, sourceProvider);
  22. int i2 = this.getOrder(o2, sourceProvider);
  23. return Integer.compare(i1, i2);
  24. }
  25. }
  26. private int getOrder(@Nullable Object obj, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
  27. Integer order = null;
  28. if (obj != null && sourceProvider != null) {
  29. Object orderSource = sourceProvider.getOrderSource(obj);
  30. if (orderSource != null) {
  31. if (orderSource.getClass().isArray()) {
  32. Object[] sources = ObjectUtils.toObjectArray(orderSource);
  33. Object[] var6 = sources;
  34. int var7 = sources.length;
  35. for(int var8 = 0; var8 < var7; ++var8) {
  36. Object source = var6[var8];
  37. order = this.findOrder(source);
  38. if (order != null) {
  39. break;
  40. }
  41. }
  42. } else {
  43. order = this.findOrder(orderSource);
  44. }
  45. }
  46. }
  47. return order != null ? order.intValue() : this.getOrder(obj);
  48. }
  49. protected int getOrder(@Nullable Object obj) {
  50. if (obj != null) {
  51. Integer order = this.findOrder(obj);
  52. if (order != null) {
  53. return order.intValue();
  54. }
  55. }
  56. return 2147483647;
  57. }
  58. @Nullable
  59. protected Integer findOrder(Object obj) {
  60. return obj instanceof Ordered ? ((Ordered)obj).getOrder() : null;
  61. }
  62. @Nullable
  63. public Integer getPriority(Object obj) {
  64. return null;
  65. }
  66. public static void sort(List<?> list) {
  67. if (list.size() > 1) {
  68. list.sort(INSTANCE);
  69. }
  70. }
  71. public static void sort(Object[] array) {
  72. if (array.length > 1) {
  73. Arrays.sort(array, INSTANCE);
  74. }
  75. }
  76. public static void sortIfNecessary(Object value) {
  77. if (value instanceof Object[]) {
  78. sort((Object[])((Object[])value));
  79. } else if (value instanceof List) {
  80. sort((List)value);
  81. }
  82. }
  83. @FunctionalInterface
  84. public interface OrderSourceProvider {
  85. @Nullable
  86. Object getOrderSource(Object var1);
  87. }
  88. }

我们先来重点看一下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的最大值,也就是最低优先级。

  1. protected int getOrder(@Nullable Object obj) {
  2. if (obj != null) {
  3. Integer order = this.findOrder(obj);
  4. if (order != null) {
  5. return order.intValue();
  6. }
  7. }
  8. return 2147483647;
  9. }

总结

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

一文带你了解Spring核心接口Ordered的实现及应用的更多相关文章

  1. 一文带你了解 Spring 5.0 WebFlux 应用场景

    一.什么是 Spring WebFlux 下图截自 Spring Boot 官方网站: 结合上图,在了解 Spring WebFlux 之前,我们先来对比说说什么是 Spring MVC,这更有益我们 ...

  2. 一文带你认识Spring事务

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y Spring事务管理我相信大家都用得很多,但可能仅仅 ...

  3. 一文带你深入浅出Spring 事务原理

    Spring事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的.对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: 获 ...

  4. 一文带你掌握Spring Web异常处理方式

    一.前言 大家好,我是 去哪里吃鱼 ,也叫小张. 最近从单位离职了,离开了五年多来朝朝夕夕皆灯火辉煌的某网,激情也好悲凉也罢,觥筹场上屡屡物是人非,调转过事业部以为能换种情绪,岂料和下了周五的班的前同 ...

  5. spring 核心接口之 Ordered

    Spring中提供了一个Ordered接口.从单词意思就知道Ordered接口的作用就是用来排序的.Spring框架是一个大量使用策略设计模式的框架,这意味着有很多相同接口的实现类,那么必定会有优先级 ...

  6. 一文带你认识Java8中接口的默认方法

    Java8是Oracle于2014年3月发布的一个重要版本,其API在现存的接口上引入了非常多的新方法. 例如,Java8的List接口新增了sort方法.在Java8之前,则每个实现了List接口的 ...

  7. Spring核心接口之InitializingBean

    一.InitializingBean接口说明 InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bea ...

  8. 【项目实践】一文带你搞定Spring Security + JWT

    以项目驱动学习,以实践检验真知 前言 关于认证和授权,R之前已经写了两篇文章: [项目实践]在用安全框架前,我想先让你手撸一个登陆认证 [项目实践]一文带你搞定页面权限.按钮权限以及数据权限 在这两篇 ...

  9. JDBC、ORM、JPA、Spring Data JPA,傻傻分不清楚?一文带你厘清个中曲直,给你个选择SpringDataJPA的理由!

    序言 Spring Data JPA作为Spring Data中对于关系型数据库支持的一种框架技术,属于ORM的一种,通过得当的使用,可以大大简化开发过程中对于数据操作的复杂度. 本文档隶属于< ...

随机推荐

  1. 数据挖掘入门系列教程(十)之k-means算法

    简介 这一次我们来讲一下比较轻松简单的数据挖掘的算法--K-Means算法.K-Means算法是一种无监督的聚类算法.什么叫无监督呢?就是对于训练集的数据,在训练的过程中,并没有告诉训练算法某一个数据 ...

  2. 深入理解kestrel的应用

    1 前言 之所以写本文章,是因为在我停止维护多年前写的NetworkSocket组件两年多来,还是有一些开发者在关注这个项目,我希望有类似需求的开发者明白为什么要停止更新,可以使用什么更好的方式来替换 ...

  3. tensorflow1.0 dropout层

    """ Please note, this code is only for python 3+. If you are using python 2+, please ...

  4. 负载均衡服务之HAProxy基础配置(四)

    前文我们聊了haproxy的状态页配置,状态页中显示各参数的含义,以及基于cookie做会话保持的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12776 ...

  5. Java锁之自旋锁

    Java锁之自旋锁 自旋锁:spinlock,是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU 原来提到的比较并交换,底层 ...

  6. BUAA_OO 第二单元总结

    作业分析 第一次作业 本次作业是单次可捎带电梯的设计,主要是初步了解多线程的设计实现和测试,本身算法设计非常简单.这次作业整体来说不是很难,是多线程的入门,主要目的就是让我们认识,了解一下什么是多线程 ...

  7. 关于foreach总是报错invalid param等问题

    原因为被foreach的数组可能为空,则会报错,只需做好容错即可,例如 if ( !empty( $arr ) ) { foreach ( $arr as $k => $v ) { } }

  8. 小白也能轻松上手的Prometheus教程

    这篇文章将承接此前关于使用Prometheus配置自定义告警规则的文章.在本文中,我们将demo安装Prometheus的过程以及配置Alertmanager,使其能够在触发告警时能发送邮件,但我们将 ...

  9. QT bug ig9icd64.dll

    QT bug ig9icd64.dll bugintel ig9icd64.dll 处有未经处理的异常 遇到了一个 奇奇怪怪的bug, 一般的QT程序中 在main.cpp 中初始化一个窗口进行显示后 ...

  10. MYSQL 索引汇总

    1.MySQL索引类型 先分以下类,MYQL有两大类索引:聚集索引和非聚集索引(只考虑mysql innodb) 聚集索引:在有主键的情况下,主键为聚集索引,其他都是非聚集索引             ...