本文是Spring IoC容器技术介绍系列文章之一。本文介绍bean对象排序概念、方法和实现。
1 背景
Spring框架中可以管理多个类型相同的bean对象,这有两层原因。
-
Spring支持对
Collection
对象的注入,这本身就意味着Spring可以管理多个相同类型的bean对象; -
Java是一种OOP语言,天然存在着继承和多态。不同类型的bean对象自然也就可能继承自相同的父类。从IoC容器中拿到的是单个对象,还是多个对象的集合,则取决于使用者从继承链上的哪个层级获取对象了。
在某些场景,当获取到的bean对象有多个时,避免不了需要对这些对象进行排序。比如,对于过滤器Filter
来讲,执行顺序是很重要的,不同执行顺序得到的最后结果可能会有所不同。
Spring对bean对象的排序提供了一套标准的解决方案。
2 概念
Spring中和排序相关的概念有三个,一个是@Order
注解,一个是Ordered
接口,还有一个是PriorityOrdered
接口。
-
@Order
注解:bean对象需要使用该注解进行标注,并提供属性int value() default Ordered.LOWEST_PRECEDENCE
-
Ordered
接口:bean对象需要继承该接口,并实现int getOrder()
方法; -
PriorityOrdered
接口,该接口继承自Ordered
,Bean对象需要继承该接口,并重写int getOrder()
方法。
上述三种方式,bean对象只需要选择一个就可以。当Spring对bean对象列表进行排序时,会根据如下逻辑决定对象顺序:
-
若两个对象中,一个是
PriorityOrdered
类型,另一个不是PriorityOrdered
类型,则PriorityOrdered
类型的是先序元素 -
若1中条件不满足,则通过如下方式获取排序值
2.1. 判断对象是否是
Orderd
类型。若是,则调用int getOrder()
获取排序值2.2. 若2.1不满足,则判断对象是否有
@Order
注解。若有,则拿到注解的value值作为排序值返回2.3 若上述都不满足,则返回
Ordered.LOWEST_PRECEDENCE
-
比较两个bean对象在2中得到的排序值,值小的是先序元素
先序元素指在排序列表中靠前的元素。
Spring提供了工具类类用于实现上述算法,工具类是AnnotationAwareOrderComparator
,方法是public static void sort(List<?> list)
。
3 源码解释
排序算法使用jdk自带的default void sort(Comparator<? super E> c)
。这个方法是List类
自带的排序方法,需要传入一个Comparator
类型的对象。这个Comparator
对象会在排序时被调用,用于确定两个元素的先后序关系。
因此,bean排序机制的核心逻辑在于如何实现这个Comparator
的逻辑。
下面这段代码实现了比较逻辑,算法逻辑和上面介绍的一致,不展开说了。
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);
}
如下这段是getOrder()
方法:
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()) {
Object[] sources = ObjectUtils.toObjectArray(orderSource);
for (Object source : sources) {
order = findOrder(source);
if (order != null) {
break;
}
}
}
else {
order = findOrder(orderSource);
}
}
}
return (order != null ? order : getOrder(obj));
}
6 总结
本文主要介绍了Spring IoC容器中,如何对bean对象列表进行排序。