巧用Spring中的@Order进行排序
作者:Jon 发布时间:2023-07-13 05:53:48
Spring @Order进行排序
直接上代码
public class OrderAnnotationTest {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
List<Object> orderList = new ArrayList<>(3);
orderList.add(a);
orderList.add(b);
orderList.add(c);
orderList.sort(AnnotationAwareOrderComparator.INSTANCE);
System.out.println(orderList);
}
@Order(0)
static class A {
@Override
public String toString() {
return "A";
}
}
@Order(-1)
static class B {
@Override
public String toString() {
return "B";
}
}
@Order(2)
static class C {
@Override
public String toString() {
return "C";
}
}
}
结果如下:
[B, A, C]
原理解析:
AnnotationAwareOrderComparator继承自OrderComparator
实际比较的方法如下
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
if (p1 && !p2) {
return -1;
}
else if (p2 && !p1) {
return 1;
}
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
Spring中关于Order的那点事
本文阅读源码版本为spring5.3.1
为啥要用Order
spring是一个大量使用策略设计模式的框架,这意味着有很多相同接口的实现类,如果不手动指定顺序的话,那么使用时肯定会有问题。而Order给我们提供了一种编码设置顺序的可能。
关于Order
spring中提供了多种方式来设置优先级,有Ordered,PriorityOrdered接口,有Order注解,除此之外,spring4.1以后,还可以使用Priority注解。下面我将针对这几种用法从源码的角度来进行分析。
Ordered,PriorityOrdered接口
public interface Ordered {
/**
* 最高优先值
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* 最低优先值
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
PriorityOrdered继承了Ordered,但并未提供任何方法,这是一个标记了优先级的接口,和Ordered相比,PriorityOrdered就是高人一等,spring中提供了比较器OrderComparator,可以通过构建一个OrderComparator,调用其compare方法,不过OrderComparator提供了一个静态sort方法,我们无需自己构建OrderComparator了,排序的结果按照order值从小到大排序。
demo
public class OrderDemo{
private final OrderComparator comparator = new OrderComparator();
@Test
void comparePriorityOrderedInstanceToStandardOrderedInstanceWithSamePriority() {
assertThatPriorityOrderedAlwaysWins(new StubPriorityOrdered(100), new StubOrdered(100));
}
@Test
void comparePriorityOrderedInstanceToStandardOrderedInstanceWithLowerPriority() {
assertThatPriorityOrderedAlwaysWins(new StubPriorityOrdered(100), new StubOrdered(200));
}
@Test
void compareOrderedInstancesBefore() {
assertThat(this.comparator.compare(new StubOrdered(100), new StubOrdered(2000))).isEqualTo(-1);
}
@Test
void compareOrderedInstancesNullFirst() {
assertThat(this.comparator.compare(null, new StubOrdered(100))).isEqualTo(1);
}
@Test
void compareOrderedInstancesNullLast() {
assertThat(this.comparator.compare(new StubOrdered(100), null)).isEqualTo(-1);
}
@Test
void test1() {
assertThat(this.comparator.compare(new Object (), new StubOrdered(2000))).isEqualTo(1);
}
private static class StubOrdered implements Ordered {
private final int order;
StubOrdered(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
private static class StubPriorityOrdered implements PriorityOrdered {
private final int order;
StubPriorityOrdered(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
}
小结
PriorityOrdered优先级比Ordered高,与设置的order值无关。
若两个对象都实现了Ordered或PriorityOrdered接口,那么设置的order值越小,优先值越高。
若没有实现Ordered或PriorityOrdered接口,默认是最低的优先级。
OrderComparator#compare解读
在看compare之前,我觉得将OrderSourceProvider这个函数式接口放在前面讲解一下,阅读源码时会更清晰一点。
@FunctionalInterface
public interface OrderSourceProvider {
/**
* 对给定对象校验并返回一个新的对象
*/
@Nullable
Object getOrderSource(Object obj);
}
demo
public class OrderDemo{
private final OrderComparator comparator = new OrderComparator();
private static class TestSourceProvider implements OrderComparator.OrderSourceProvider {
private final Object target;
private final Object orderSource;
TestSourceProvider(Object target, Object orderSource) {
this.target = target;
this.orderSource = orderSource;
}
@Override
public Object getOrderSource(Object obj) {
if (target.equals(obj)) {
return orderSource;
}
return null;
}
}
@Test
void compareWithSourceProviderArray() {
Comparator<Object> customComparator = this.comparator.withSourceProvider(
new TestSourceProvider(5L, new Object[] {new StubOrdered(10), new StubOrdered(-25)}));
assertThat(customComparator.compare(5L, new Object())).isEqualTo(-1);
}
@Test
void compareWithSourceProviderArrayNoMatch() {
Comparator<Object> customComparator = this.comparator.withSourceProvider(
new TestSourceProvider(5L, new Object[] {new Object(), new Object()}));
assertThat(customComparator.compare(new Object(), 5L)).isEqualTo(0);
}
@Test
void compareWithSourceProviderEmpty() {
Comparator<Object> customComparator = this.comparator.withSourceProvider(
new TestSourceProvider(50L, new Object()));
assertThat(customComparator.compare(new Object(), 5L)).isEqualTo(0);
}
}
接下来我们来阅读compare源码。
public int compare(@Nullable Object o1, @Nullable Object o2) {
return doCompare(o1, o2, null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
// 这里会判断是否实现了PriorityOrdered接口
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
// 这里会看到根本没有比较order的值,只要实现PriorityOrdered接口,就会排在前面
if (p1 && !p2) {
return -1;
}else if (p2 && !p1) {
return 1;
}
// 获取对象设置的order值
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
Integer order = null;
if (obj != null && sourceProvider != null) {
Object orderSource = sourceProvider.getOrderSource(obj);
if (orderSource != null) {
// 如果返回的是数组
if (orderSource.getClass().isArray()) {
for (Object source : ObjectUtils.toObjectArray(orderSource)) {
// 只要找到对象设置的order值,就跳出
order = findOrder(source);
if (order != null) {
break;
}
}
}else {
order = findOrder(orderSource);
}
}
}
// 如果我们没有提供OrderSourceProvider
return (order != null ? order : getOrder(obj));
}
protected int getOrder(@Nullable Object obj) {
if (obj != null) {
Integer order = findOrder(obj);
if (order != null) {
return order;
}
}
// object为null时,返回值最大
return Ordered.LOWEST_PRECEDENCE;
}
protected Integer findOrder(Object obj) {
// 没有实现Ordered接口将返回null
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
}
@Order与@Priority
spring中提供了对@Order与@Priority支持的比较器AnnotationAwareOrderComparator,该类继承OrderComparator,并覆盖了findOrder方法,我们来一起看下源码。
protected Integer findOrder(Object obj) {
Integer order = super.findOrder(obj);
if (order != null) {
return order;
}
// 调用父类的findOrder方法无法找到设定的order值时
return findOrderFromAnnotation(obj);
}
private Integer findOrderFromAnnotation(Object obj) {
AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
// 对整个类型层次结构执行完整搜索,包括父类和接口
MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
// 获取注解中设置的order值
Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
if (order == null && obj instanceof DecoratingProxy) {
return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
}
return order;
}
static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) {
if (!(element instanceof Class)) {
return findOrder(annotations);
}
// 加入缓存中
Object cached = orderCache.get(element);
if (cached != null) {
return (cached instanceof Integer ? (Integer) cached : null);
}
Integer result = findOrder(annotations);
orderCache.put(element, result != null ? result : NOT_ANNOTATED);
return result;
}
// 没有找到Order注解后才去寻找@Priority注解
private static Integer findOrder(MergedAnnotations annotations) {
MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
if (orderAnnotation.isPresent()) {
return orderAnnotation.getInt(MergedAnnotation.VALUE);
}
MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
if (priorityAnnotation.isPresent()) {
return priorityAnnotation.getInt(MergedAnnotation.VALUE);
}
return null;
}
demo
public class AnnotationAwareOrderComparatorTests {
@Test
void sortInstancesWithSubclass() {
List<Object> list = new ArrayList<>();
list.add(new B());
list.add(new C());
AnnotationAwareOrderComparator.sort(list);
assertThat(list.get(0) instanceof C).isTrue();
assertThat(list.get(1) instanceof B).isTrue();
}
@Test
void sortInstancesWithOrderAndPriority() {
List<Object> list = new ArrayList<>();
list.add(new B());
list.add(new A2());
AnnotationAwareOrderComparator.sort(list);
assertThat(list.get(0) instanceof A2).isTrue();
assertThat(list.get(1) instanceof B).isTrue();
}
@Order(1)
private static class A {
}
@Order(2)
private static class B {
}
private static class C extends A {
}
@Priority(1)
private static class A2 {
}
}
小结
@Order与@Priority注解放置在类,接口或参数上,可以被继承;它们之间是可以互相替换的关系。
应用
spring源码中有很多地方都显式的调用AnnotationAwareOrderComparator的sort方法,也有一些地方调用的OrderComparator的sort方法,大家自己可以找找看。
我这里发现了一点有意思的地方,我们如果定义多个ControllerAdvice的bean,分别通过实现Ordered,PriorityOrdered接口来定义执行时的顺序,会发现上面我们总结的 PriorityOrdered优先级就是比Ordered高 这一点不成立,其实只是spring将ControllerAdvice相关信息封装了一下欺骗了我们。我看的源码的版本是5.3.1,低于5.2版本的不会发生这样的事情。这里我们就来看看5.2版本前后源码有哪些变化,导致了这个现象的发生。
这里就拿RequestMappingHandlerAdapter初始化去寻找ControllerAdvice注解的代码来举例
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
// 5.2版本前使用下面注释的这行代码,5.2之后这行代码就去掉了,而是在上面findAnnotatedBeans
// 方法中使用OrderComparator.sort(adviceBeans)
//AnnotationAwareOrderComparator.sort(adviceBeans);
...
}
我们知道OrderComparator适用范围是比AnnotationAwareOrderComparator要窄一点的,它不支持注解,那么上面这样的改动是不是就意味着我们定义ControllerAdvice时,就不能使用@Order与@Pri-ority呢?
其实它是支持的,ControllerAdviceBean#findAnnotatedBeans方法中会将我们定义的Con-trollerAdvice类包装成ControllerAdviceBean,而ControllerAdviceBean是实现了Ordered接口的,那么OrderComparator#sort方法要想支持使用注解,ControllerAdviceBean的getOrder方法中就必须干点啥,分析了挺多,我们还是看源码实现吧。
// 5.2版本后
public int getOrder() {
if (this.order == null) {
String beanName = null;
Object resolvedBean = null;
// 这里根据beanName获取bean
if (this.beanFactory != null && this.beanOrName instanceof String) {
beanName = (String) this.beanOrName;
String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName);
if (!isScopedProxy && !ScopedProxyUtils.isScopedTarget(beanName)) {
resolvedBean = resolveBean();
}
}else {
resolvedBean = resolveBean();
}
// 这里只判断了是否实现了Ordered接口,并没有对实现PriorityOrdered作特殊处理
// 这里优先判断是否实现了Ordered接口,如果同时使用注解的话将被忽略
if (resolvedBean instanceof Ordered) {
this.order = ((Ordered) resolvedBean).getOrder();
}else {
if (beanName != null && this.beanFactory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) this.beanFactory;
try {
BeanDefinition bd = cbf.getMergedBeanDefinition(beanName);
if (bd instanceof RootBeanDefinition) {
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
if (factoryMethod != null) {
// 这里将会从注解@Order与@Priority中获取order值
this.order = OrderUtils.getOrder(factoryMethod);
}
}
}catch (NoSuchBeanDefinitionException ex) {
// ignore -> probably a manually registered singleton
}
}
if (this.order == null) {
if (this.beanType != null) {
this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE);
}
else {
this.order = Ordered.LOWEST_PRECEDENCE;
}
}
}
}
return this.order;
}
源码分析后,我们来看一段测试demo
public class ControllerAdviceBeanTests {
@ControllerAdvice
@Order(100)
@Priority(200)
static class OrderedControllerAdvice implements Ordered {
@Override
public int getOrder() {
return 42;
}
}
@ControllerAdvice
// Order和@Priority由于Order的实现应该被忽略
@Order(100)
@Priority(200)
static class PriorityOrderedControllerAdvice implements PriorityOrdered {
@Override
public int getOrder() {
return 55;
}
}
@Configuration(proxyBeanMethods = false)
static class Config {
@Bean
OrderedControllerAdvice orderedControllerAdvice() {
return new OrderedControllerAdvice();
}
@Bean
PriorityOrderedControllerAdvice priorityOrderedControllerAdvice() {
return new PriorityOrderedControllerAdvice();
}
}
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void findAnnotatedBeansSortsBeans() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(context);
// 输出顺序并不是 55 42,而是42,55
for (ControllerAdviceBean adviceBean : adviceBeans) {
System.out.println (adviceBean.getOrder ());
}
}
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:https://blog.csdn.net/JonKee/article/details/103981464


猜你喜欢
- 本篇文章介绍:如何使用Toolbar;自定义Toolbar;先来看一看效果,了解一下toolbar;布局文件:<android.sup
- 为什么不用SQLite? 原因多种:除了面向对象和关系数据库之间的阻抗不匹配时,SQLite可能是矫枉过正(带来了更多的开销)对于一些简单的
- 对流进行操作时要引用 using System.IO; 命名空间 FileStream常用的属性和方法:属性:CanRead 判断当前流是否
- 本文实例为大家分享了C#实现学生成绩管理系统的具体代码,供大家参考,具体内容如下使用链表写学生成绩管理系统链表可以灵活的展示增删改查下面是结
- 1、首先,找到 Android SDK 在本机中的位置,如果不知道,可以通过在 Android Studio 找到,如下:2、其次,通过 c
- 前言在服务器上,当我们启动了tomcat,就可以以http://ip地址:8080/文件路径/文件名的方式,进行访问到我们服务器上处于tom
- 前言兄弟们,刚刚又给seata社区修了一个BUG,有用户提了issue反应TransactionHook在某些情况下不会被调用:相关issu
- 本文实例讲述了C#简单实现显示中文格式星期几的方法。分享给大家供大家参考,具体如下:1.DateTime.Now.ToString(&quo
- 根据之前学的Android对话框技术,来实现下面一个效果:界面有一个"退出"按钮,按下之后会弹出一个询问是否退出的提示对
- 在源代码中设置断点,然后进行点击调试若要启用反汇编窗口,请在工具>选项(或工具> 选项>调试下,选择启用地址级调试。若要在
- 我们在设计layout的时候,使用Split视图,就是左侧是代码,右侧是设计图,但是我们忽视了最上方的工具栏,这里才是真正的宝藏。下面教大家
- 在网上也没有找到好的解决方案,于是自己研究了下给解决了,分享给大家,希望对大家能有所帮助。一、异常信息这种情况是因为FTP设置的默认目录引发
- 文件写入为提供相对较高性能的文件读写操作,这里果断选择了 NIO 对文件的操作,因为业务背景需要数据的安全落盘。这里主要采用 ByteBuf
- 一 :问题背景问题:当查询接口较复杂时候,数据的获取都需要[远程调用],必然需要花费更多的时间。 假如查询文章详情页面,需要如下标注的时间才
- 为了实现自定义的Menu和ContextMenu效果,下面演示代码通过派生ProfessionalColorTable类,在自定义的类中重写
- 本文实例为大家分享了RxJava Retrofit实现购物车展示的具体代码,供大家参考,具体内容如下先给大家展示一下效果图框架结构: 1.项
- 官方办法 JAVA语言提供的一个关键字“FINAL”可以用来履行该任务。看看下面的源代码范例://FinalDemo.java public
- 代码import javax.mail.internet.InternetAddress;import javax.mail.interne
- 1. xml文件中加入自定义 搜索view<com.etoury.etoury.ui.view.IconCenterEditText
- 今天有朋友问我Struts2中Action必须实现execute方法吗?顺利的回答出来了。其实分两种情况:1)如果你的Action类是继承自