Spring源码必备-@Order排序详解

1,354 阅读4分钟

大家都知道Spring中可以使用@Order和@Priority来决定SpringBean的启动顺序,但是你知道他是怎么实现的吗?

下面我们就来看看Spring是怎么设计实现的。

一、如何使用

我们先看看Spring是如何使用的,然后再深入内部去看。

public class SpringApplication {
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        //排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
}

//进行排序的类
public class AnnotationAwareOrderComparator extends OrderComparator {
	public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
	public static void sort(List<?> list) {
		if (list.size() > 1) {
			list.sort(INSTANCE);
		}
	}
}

以上代码是SpringBoot启动刚开始的时候的一段代码,我加了注释的那行就是进行排序的。

该类的结构关系如下:

 Spring中排序使用的就是AnnotationAwareOrderComparator类,可以看出上层接口是Comparator,java的比较器我们都很熟悉了,这里就不多说了。

我们自己再写个小例子看看:

public class Test {
    public static void main(String[] args){
        List<A> list = new ArrayList<>();
        list.add(new A2());
        list.add(new A3());
        list.add(new A1());
        AnnotationAwareOrderComparator.sort(list);
        for (A a : list){
            a.say();
        }
    }
}

interface A{
    void say();
}
@Order(1)
class A1 implements A{
    public void say(){
        System.out.println(1);
    }
}
@Order(2)
class A2 implements A{
    public void say(){
        System.out.println(2);
    }
}
@Priority(3)
class A3 implements A{
    public void say(){
        System.out.println(3);
    }
}

以上代码写的是按不同顺序放入list,排序后,按照@Order和@Priority标注的顺序就行了排序。 

二、相关抽象类解释

Java是面向对象的语言,我们千万不能忘记这一点。我们可以把任何概念或者事物抽象成一个对象,Spring也不例外,所以,我们了解他的某一块功能,要先去了解Spring为了实现这个功能抽象出了哪些类。

2.1 @Order和@Priority

@Target({TYPE,PARAMETER})
@Retention(RUNTIME)
@Documented
public @interface Priority {
    int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
	int value() default Ordered.LOWEST_PRECEDENCE;

}

这两个注解的区别就是,@Order有一个默认值,他两的标注范围也不同。

从之前的自己写的小例子也可以看出,这两个注解是被排序功能识别的,并不需要相关对象注入到Spring容器中。

2.2  查找策略SearchStrategy

Spring排序是实现了java的比较器Comparator,所以必须获取注解上表示大小的值。有查找就有了策略,是只查找当前类还是需要查找父类。

	enum SearchStrategy {
		/**
		 * 只查找当前类,不考虑@Inherited、父类和实现的接口
		 */
		DIRECT,
		/**
		 * 查找当前类和@Inherited,不查找父类和实现的接口
		 */
		INHERITED_ANNOTATIONS,
		/**
		 * 查找当前类和父类,不查找实现的接口
		 */
		SUPERCLASS,
		/**
		 * 查找当前类、父类、实现的接口
		 */
		TYPE_HIERARCHY,
		/**
		 * 在TYPE_HIERARCHY基础上还查找外部类(如果当前类是内部类)
		 */
		TYPE_HIERARCHY_AND_ENCLOSING_CLASSES
	}

 小知识:@Inherited 子类会继承父类的注解。实现类不会继承接口的注解、子接口不会继承父接口的注解

2.3 RepeatableContainers

 小知识:

@Repeatable:正常情况下同一个注解只能标注一次,而@Repeatable可以实现标注多次

如果出现下面这种情况,就得使用该类来解决了:

@Repeatable(persons.class)
@Order
@interface person{
    String value();
}

@interface persons{
    person[] value();
}

@persons({@person("a"), @person("b")})
class A{
}
@person("a")
@person("b")
class B{
}

 如果有自定义注解比如@person,那么获取A类的Order值就很麻烦了。RepeatableContainers就是做这个事的。它负责最终找出@Order注解。

2.4 注解过滤器AnnotationFilter

 这个类很简单,只有一个实现类,该类用来排除不需要查找的注解。

@FunctionalInterface
public interface AnnotationFilter {
	AnnotationFilter PLAIN = packages("java.lang", "org.springframework.lang");
	static AnnotationFilter packages(String... packages) {
		return new PackagesAnnotationFilter(packages);
	}
}

final class PackagesAnnotationFilter implements AnnotationFilter {
	@Override
	public boolean matches(String annotationType) {
		for (String prefix : this.prefixes) {
			if (annotationType.startsWith(prefix)) {
				return true;
			}
		}
		return false;
	}
}

如上代码,Spring中排除了java.lang和org.springframework.lang下的注解。

三、整个过程

整个排序过程,最重要也是最不容易阅读的代码就是查找Order值的过程,入口方法如下:

public class OrderComparator implements Comparator<Object> {
	@Nullable
	protected Integer findOrder(Object obj) {
		return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
	}
}

public class AnnotationAwareOrderComparator extends OrderComparator {	
    @Override
	@Nullable
	protected Integer findOrder(Object obj) {
		Integer order = super.findOrder(obj);
		if (order != null) {
			return order;
		}
		return findOrderFromAnnotation(obj);
	}

	@Nullable
	private Integer findOrderFromAnnotation(Object obj) {
		AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
		MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
		Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
		if (order == null && obj instanceof DecoratingProxy) {
			return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
		}
		return order;
	}
}

首先调用父类方法,是否是Ordered接口的实现类,如果是,则直接调用方法获取值,如果不是则调用子方法。

子方法第一步首先获取Class对象。

AnnotatedElement是Class的父类。

第二步,查找前的准备工作。

第三步,从所有注解中过滤出order值。

第四部,如果找到直接返回,找不到并且是代理类的话,用目标对象再次走一遍刚才的流程。

代码中最重要的有2个

  • 查找前的准备工作。
  • OrderUtils类负责从注解中查找出order值。

3.1 准备工作

从静态方法from进入,我们可以看到初始化了以下内容:

  1. 策略选择的是TYPE_HIERARCHY
  2. 容器选择的是StandardRepeatableContainers
  3. Filter选择排除的是"java.lang", "org.springframework.lang"

3.2 过滤

这部分的代码量最大,这里不详细展开,大家可以自己简单看看,看的方法还是和之前一样,按功能模块看,不要从头到尾一直往下读。

	@Nullable
	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;
	}

以上为外层方法,看着很容易,调用MergedAnnotations的get方法先查找Order注解的值,有就返回,没有则查找javax.annotation.Priority注解的值。

四、总结

如果大家一边翻看源码,一边看本文,应该对Spring如何实现Order很了解了。

一个简单的Order,涉及到的代码和抽象概念都很多,包括了过滤、策略、Repeatable容器查找等,如果你详细看的话还会发现别的抽象接口,比如Finder什么的,不过那是更细节的代码了。


欢迎讨论交流