大家都知道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进入,我们可以看到初始化了以下内容:
- 策略选择的是TYPE_HIERARCHY
- 容器选择的是StandardRepeatableContainers
- 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什么的,不过那是更细节的代码了。
欢迎讨论交流