这篇文章主要介绍了巧用Spring中的@Order进行排序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
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 ());
}
}
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程学习网。
本文标题为:巧用Spring中的@Order进行排序
基础教程推荐
- Java数据结构之对象比较详解 2023-03-07
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- Java文件管理操作的知识点整理 2023-05-19
- Java实现查找文件和替换文件内容 2023-04-06
- java基础知识之FileInputStream流的使用 2023-08-11
- java实现多人聊天系统 2023-05-19
- Java实现线程插队的示例代码 2022-09-03
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- Java并发编程进阶之线程控制篇 2023-03-07
- springboot自定义starter方法及注解实例 2023-03-31