如果我问你 Spring Order 在生产有什么应用,你又该如何应对呢?

👈👈👈 欢迎点赞收藏关注哟

首先分享之前的所有文章 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164...
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca...

一. 前言

之前看了基于 @Configuration 的Bean 优先级控制,这还不够,在 Spring 中还有很多种优先级控制的方式。

这一篇就来说说关于 Order 在实际应用中应该怎么玩,以及他的问题以及原理。

二. 基本使用

在 Spring 中的 Order 特性有2种实现 :

  • 基于 @Order 注解对Bean优先级做控制
  • 基于 Order接口实现 Bean 优先级控制
java 复制代码
@Component
@Order(2)
public class SecondComponent implements Ordered {
        
    // 如果是实现的接口,则需要继承对应的方法    
    @Override
    public int getOrder() {
        return 1;
    }

}

三. 简单看原理

3.1 先从微观看原理

先从微观入手,Order 处理流程里面最核心的类就是 AnnotationAwareOrderComparator ,在这个类里面会进行 Order 的查找和比较 ,核心源码就一个 :

java 复制代码
protected Integer findOrder(Object obj) {
   // 如果是实现的 Order 接口 ,则通过 Order 接口的 getOrder 方法
   Integer order = super.findOrder(obj);
   if (order != null) {
      return order;
   }
   // 尝试通过注解获得 Order 优先级
   return findOrderFromAnnotation(obj);
}

// 补充一 : 注解获取优先级的流程
OrderUtils.getOrderFromAnnotations(element, annotations)

private static Integer findOrder(MergedAnnotations annotations) {

	// 核心一 : 如果拿到了 Order 注解就直接返回
	MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
	
	// 核心二 : 如果没有拿到 Order 注解会去获取 "javax.annotation.Priority"
	MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
}

- 细节点 : 在 OrderUtils 中会对注解值进行缓存
private static final Map<AnnotatedElement, Object> orderCache = new ConcurrentReferenceHashMap<>(64);
目的 : 


- 细节点 : javax.annotation.Priority 注解有什么用
Priority 对应的是 @Priority(2) 注解 。

3.2 从宏观看流程

SpringOrder 的使用最后还是集中到 AnnotationAwareOrderComparator 的调用上面,从使用的方法上就不难看出使用的场景 :

Order 在这些流程里面是怎么控制顺序的

我们以 Spring.factories 流程的加载流程 :(createSpringFactoriesInstances)

这也是 Order 最常见的用法之一,当我们通过 Spring.factories 定义自动配置的工厂方法时,在获取到所有的 Factories 类后,会调用 AnnotationAwareOrderComparator.sort(instances) 对这些工厂方法排序

四. 业务使用场景

4.1 Configuration 上面的使用

java 复制代码
@Slf4j
@Configuration
public class ConfigurationOrder {

    @Bean
    @Order(10)
    public ConfigBeanB getConfigBeanB() {
        return new ConfigBeanB();
    }

    @Bean
    @Order(1)
    public ConfigBeanA getConfigBeanA() {
        return new ConfigBeanA();
    }

}
  • 该场景下 Order 只支持同一个 @Configuration 的排序
  • 不能 通过在 @Configuration 类上添加 @Order 实现 Configuration Bean 的排序
  • 不能 通过 @Component + Order 接口 或者 @Order 注解实现Bean 控制

也就是说 ,以下这种方式是不会生效的

4.2 Spring.factories 启动类通过 Order 排序

java 复制代码
public class SelfApplicationContextInitializer implements ApplicationContextInitializer, Ordered {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
    }

    @Override
    public int getOrder() {
        return 99;
    }
}

基于 Spring.factories 的自动配置是可以通过 Order 定义优先级的

4.3 AOP 类使用 Order

Aop 中可能在业务场景中不会特意定义 Order ,一般一个切面对应的就是一个业务,但是如果是一些开源框架或者原生代码的 AOP ,又想在他们切面之前做一些定制,那么这个就有必要了 :

java 复制代码
@Aspect
@Component
@Order(33)
public class AspectOrder {

    @Around("execution(* com.test.bean.demo.service.BeanOne.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("AspectOrder 执行目标方法之前的操作");
        Object result = joinPoint.proceed();
        return result;
    }
}

4.4 Listener 中使用 Order

java 复制代码
@Component
@Order(11)
public class FirstListener implements ApplicationListener<SpringApplicationEvent> {
    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        log.info("FirstListener");
    }
}
  • 只不过不同于 Bean 加载的流程 ,Listener Order 会自己去拿方法的 Order

4.5 影响 ApplicationRunner 的使用

  • ❗注意 : @Order 不影响 Bean 的创建过程,但是会影响 ApplicationRunner 这些依赖的执行过程
java 复制代码
@Service
@Order(1000)
public class BeanOne implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("------> BeanOne Run <-------");
    }
}

当 实现了 ApplicationRunner 接口的情况下,会按照 @Order 的顺序进行执行。

👉👉同理还有 CommandLineRunner!!

4.6 其他 Order 生效的地方

  • EnvironmentPostProcessor 的执行顺序
  • 待后续补充~~~

五. 问题

5.1 为什么 Order + @Component 不会生效

直接的原因在于 AnnotationAwareOrderComparator 没有在 @Bean 加载过程中被调用。

在 Bean 加载过程中 ,除了通过包的扫描流程控制Bean的顺序外 ,就只能通过 @DependOn 注解了。

详细可以看这一篇 # 写好 Spring Starter : 控制好Bean的加载顺序与原理

5.2 为什么那些 ApplicationRunner 中 Order 又可以生效?

java 复制代码
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    // 在这个环节中,Bean 的顺序按照Order进行排序
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        //.......
    }
}

从这里也可以推断出 ,Bean 加载时是不会按照 Order 排序的。

总结

闲时写了一篇,可能会有遗漏,但是这篇里面的都代码验证过,应该不会有啥问题。

相关推荐
码蜂窝编程官方几秒前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游
Viktor_Ye17 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm19 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
一二小选手23 分钟前
【Maven】IDEA创建Maven项目 Maven配置
java·maven
J老熊29 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
猿java34 分钟前
什么是 Hystrix?它的工作原理是什么?
java·微服务·面试
AuroraI'ncoding35 分钟前
时间请求参数、响应
java·后端·spring
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
所待.3831 小时前
JavaEE之线程初阶(上)
java·java-ee
Winston Wood1 小时前
Java线程池详解
java·线程池·多线程·性能