1. 程式人生 > >一文帶你瞭解Spring核心介面Ordered的實現及應用

一文帶你瞭解Spring核心介面Ordered的實現及應用

### 前言 最近在看框架的時候,發現了這個介面,在此進行總結,希望能夠給大家幫助,同時提升自己。 ### order介面的大體介紹 Spring框架中有這個一個介面,名字叫Ordered,聯想我們在資料庫中應用的Ordered,很容易想到它的含義就是用來排序。那麼問題來了,Spring中為什麼要定義這樣一個排序介面呢。我們知道spring框架使用了大量的策略設計模式。策略設計模式意味著我們的同一個介面,會有大量的不同實現。那麼這麼多實現,先執行哪個,後執行哪個呢。這就產生了一個排序和優先順序的問題,於是Ordered介面登場,用來解決這一問題。 ### ordered介面的正式介紹 首先我們通過spring的原始碼看一下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。 這個時候,我們在配置事務切面的時候。在xml中配置order。 ``` ``` 如果是使用注入bean的方式的話,直接實現介面和上方一樣使用即可。 這個時候,我們就會發現。切換資料來源的方法會永遠在事務之前執行,這就實現了我們的目的。 ### order註解的使用 讀到現在的讀者在想,還要實現介面感覺好麻煩啊,有沒有什麼更方便的方法呢。當然有,我們介紹一下@Order註解。 還是先看一下order註解的原始碼。 ``` @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Documented public @interface Order { int value() default 2147483647; } ``` 預設的優先順序是最小的。 我們在使用的時候,只要在類上面打上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 { public static final OrderComparator INSTANCE = new OrderComparator(); public OrderComparator() { } public Comparator 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的最大值,也就是最低優先順序。 ``` protected int getOrder(@Nullable Object obj) { if (obj != null) { Integer order = this.findOrder(obj); if (order != null) { return order.intValue(); } } return 2147483647; } ``` ### 總結 至此 ordered相關的東西就介紹到此為止,文中難免有不足,希望大家提出指正,