当前位置: 首页 > news >正文

Spring(18) @Order注解介绍、使用、底层原理

目录

    • 一、简介
    • 二、List 注入使用示例
      • 2.1 测试接口类
      • 2.2 测试接口实现类1
      • 2.3 测试接口实现类2
      • 2.4 启动类(测试)
      • 2.5 测试结果
        • 场景一:
        • 场景二:
    • 三、CommandLineRunner 使用示例
      • 3.1 接口实现类1
      • 3.2 接口实现类2
      • 3.3 测试结果
        • 场景一:
        • 场景二:
    • 四、@Order失效场景
      • 4.1 失效代码示例
      • 4.2 执行结果
      • 4.3 失效场景补救
    • 五、@Order、@Priority底层原理
      • 5.1 平平无奇的启动类
      • 5.2 神奇的 run() 方法
      • 5.3 开始排序的 callRunners() 方法
      • 5.4 排序调用图
      • 5.5 排序的根源 findOrder() 方法

一、简介

@Order:是 spring-core 包下的一个注解。@Order 作用是定义 Spring IOC 容器中 Bean 的执行顺序

注意: Spring 的 @Order 注解或者 Ordered 接口,不决定 Bean 的加载顺序和实例化顺序,只决定 Bean 注入到 List 中的顺序。

@Order 注解接受一个整数值作为参数,数值越小表示优先级越高。当存在多个具有 @Order 注解的组件时,Spring Boot将按照数值从小到大的顺序加载它们。

需要注意的是:

  • @Order 注解只能用于标记 Spring 容器中的组件,而不适用于标记普通的类。因此,在使用 @Order 注解时,确保你的组件被正确地注册到 Spring 容器中。
  • @Order 注解只决定Bean的注入顺序,并不保证实际执行的顺序。例如:在 Web 应用中,Filter 的执行顺序并不受 @Order 注解影响。如果需要确保 Filter 按照顺序执行,可以使用 FilterRegistrationBean 来设置 Filter 的顺序。

二、List 注入使用示例

包结构如下:

在这里插入图片描述

2.1 测试接口类

IOrderTest 接口中定义了一个 handle() 方法用于测试。

IOrderTest.java

/*** <p> @Title IOrderTest* <p> @Description @Order注解测试接口** @author ACGkaka* @date 2023/10/17 11:20*/
public interface IOrderTest {/*** 处理*/void handle();
}

2.2 测试接口实现类1

@Order注解测试实现类01 和 @Order注解测试实现类02 实现了 IOrderTest 接口,用于测试 @Order 的生效。

OrderTestImpl01.java

import com.demo.test.IOrderTest;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** <p> @Title OrderTestA* <p> @Description @Order注解测试实现类01** @author ACGkaka* @date 2023/10/17 11:18*/
@Order(1)
@Component
public class OrderTestImpl01 implements IOrderTest {public OrderTestImpl01() {System.out.println("=== OrderTestImpl01 constructor() ==");}@Overridepublic void handle() {System.out.println("=== OrderTestImpl01 handle() ===");}
}

2.3 测试接口实现类2

@Order注解测试实现类01 和 @Order注解测试实现类02 实现了 IOrderTest 接口,用于测试 @Order 的生效。

OrderTestImpl02.java

import com.demo.test.IOrderTest;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** <p> @Title OrderTestImpl02* <p> @Description @Order注解测试实现类02** @author ACGkaka* @date 2023/10/17 11:18*/
@Order(2)
@Component
public class OrderTestImpl02 implements IOrderTest {public OrderTestImpl02() {System.out.println("=== OrderTestImpl02 constructor() ===");}@Overridepublic void handle() {System.out.println("=== OrderTestImpl02 handle() ===");}
}

2.4 启动类(测试)

SpringbootDemoApplication.java

import com.demo.test.IOrderTest;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.util.List;@SpringBootApplication
public class SpringbootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootDemoApplication.class, args);}@Beanpublic CommandLineRunner commandLineRunner(List<IOrderTest> list) {return args -> {System.out.println("=== CommandLineRunner ===");list.forEach(IOrderTest::handle);};}
}

2.5 测试结果

场景一:
  • @Order(1) 注解修饰 OrderTestImpl01.java
  • @Order(2) 注解修饰 OrderTestImpl02.java

执行结果如下:

在这里插入图片描述

场景二:
  • @Order(1) 注解修饰 OrderTestImpl02.java
  • @Order(2) 注解修饰 OrderTestImpl01.java

执行结果如下:

在这里插入图片描述


三、CommandLineRunner 使用示例

3.1 接口实现类1

CommandLineRunner01.java

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** <p> @Title CommandLineRunner01* <p> @Description @Order注解测试01** @author ACGkaka* @date 2023/10/17 11:20*/
@Component
@Order(1)
public class CommandLineRunner01 implements CommandLineRunner {@Overridepublic void run(String... args) {System.out.println("=== CommandLineRunner01 ===");}
}

3.2 接口实现类2

CommandLineRunner02.java

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** <p> @Title CommandLineRunner02* <p> @Description @Order注解测试02** @author ACGkaka* @date 2023/10/17 11:20*/
@Component
@Order(2)
public class CommandLineRunner02 implements CommandLineRunner {@Overridepublic void run(String... args) {System.out.println("=== CommandLineRunner02 ===");}
}

3.3 测试结果

场景一:
  • @Order(1) 注解修饰 CommandLineRunner01.java
  • @Order(2) 注解修饰 CommandLineRunner02.java

执行结果如下:

在这里插入图片描述

场景二:
  • @Order(1) 注解修饰 CommandLineRunner02.java
  • @Order(2) 注解修饰 CommandLineRunner01.java

执行结果如下:

在这里插入图片描述

四、@Order失效场景

失效场景:@Configuration 里面通过 @Bean 方式创建 Bean,在上面加 @Order 控制顺序是没有效果的。

4.1 失效代码示例

SpringbootDemoApplication.java

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;@SpringBootApplication
public class SpringbootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootDemoApplication.class, args);}@Order(2)@Beanpublic CommandLineRunner commandLineRunner01() {return args -> System.out.println("=== commandLineRunner01 ===");}@Order(1)@Beanpublic CommandLineRunner commandLineRunner02() {return args -> System.out.println("=== commandLineRunner02 ===");}
}

4.2 执行结果

由下图可知,虽然我们使用 @Order 注解明确声明要先执行 commandLineRunner02,但是并没有生效。

在这里插入图片描述

4.3 失效场景补救

在 @Order 注解失效的场景下,可以通过以下方式来控制顺序:

  • 方式一: 可以通过具体实现类的方式创建 Bean,用 @Order + @Component 注解修饰。
  • 方式二: 可以通过 RegistrationBean 方式创建 Bean,用 setOrder 添加顺序。
  • 方式三: filter 可以通过 FilterRegistrationBean 创建 filter 的 Bean,用 setOrder 添加顺序。

五、@Order、@Priority底层原理

看完 @Order 注解的时候,可能会疑惑 IOC 容器时如何通过 @Order 注解来控制程序的先后顺序的,接下来我们从源码层面看下,容器是如何加载的。

先说结论:

  • @Order 底层是在 Bean 注入 IOC 容器之后执行的,所以无法控制 Bean 的加载顺序。
  • @Order 底层是通过 List.sort(Comparator) 实现的,AnnotationAwareOrderComparator 类集成 OrderComparator 类,通过获取注解的 value 值实现了比对逻辑。

5.1 平平无奇的启动类

SpringbootDemoApplication.java

@SpringBootApplication
public class SpringbootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootDemoApplication.class, args);}
}

5.2 神奇的 run() 方法

SpringApplication.run()

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);prepareContext(context, environment, listeners, applicationArguments, printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);// #### 重点!!!调用具体的执行方法 ###callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;
}

5.3 开始排序的 callRunners() 方法

SpringApplication.callRunners()

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());// ### 重点!!!按照定义的优先级顺序排序 ###AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}
}

5.4 排序调用图

由于剩下的实现内容调用链比较长,为了看起来更清晰直观,采用顺序图展现出来:

SpringApplication AnnotationAwareOrderComparator List OrderComparator OrderUtils run() callRunners() sort() sort() compare() doCompare() getOrder() findOrder() findOrderFromAnnotation() getOrderFromAnnotations() findOrder() SpringApplication AnnotationAwareOrderComparator List OrderComparator OrderUtils

5.5 排序的根源 findOrder() 方法

获取 @Order 注解的 value 值,来进行排序。

OrderUtils.findOrder()

@Nullable
private static Integer findOrder(MergedAnnotations annotations) {MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);if (orderAnnotation.isPresent()) {// ### 重点!!!获取@Order注解的value值return orderAnnotation.getInt(MergedAnnotation.VALUE);}MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);if (priorityAnnotation.isPresent()) {// ### 重点!!!获取@Priority注解的value值return priorityAnnotation.getInt(MergedAnnotation.VALUE);}return null;
}

整理完毕,完结撒花~ 🌻





参考地址:

1.浅谈Spring @Order注解的使用,https://blog.csdn.net/yaomingyang/article/details/86649072

2.深入理解Spring的@Order注解和Ordered接口,https://blog.csdn.net/zkc7441976/article/details/112548075

3.踩坑!@Order失效。。。https://blog.csdn.net/qq_34142184/article/details/126951618

相关文章:

Spring(18) @Order注解介绍、使用、底层原理

目录 一、简介二、List 注入使用示例2.1 测试接口类2.2 测试接口实现类12.3 测试接口实现类22.4 启动类&#xff08;测试&#xff09;2.5 测试结果场景一&#xff1a;场景二&#xff1a; 三、CommandLineRunner 使用示例3.1 接口实现类13.2 接口实现类23.3 测试结果场景一&…...

目标检测YOLO实战应用案例100讲-基于改进YOLOv6的轧钢表面细小缺陷检测

目录 前言 存在的问题 轧钢缺陷图像特征分析 2.1单一类型缺陷 2.2面状缺陷...

leetcode:507. 完美数(python3解法)

难度&#xff1a;简单 对于一个 正整数&#xff0c;如果它和除了它自身以外的所有 正因子 之和相等&#xff0c;我们称它为 「完美数」。 给定一个 整数 n&#xff0c; 如果是完美数&#xff0c;返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;num…...

智能物联网解决方案:蓝牙IOT主控模块打造高效监测和超低功耗

物联网蓝牙模块&#xff0c;无论单模&#xff0c;还是双模&#xff0c;或者双模音频的选择&#xff0c;如下文说描述&#xff1a; 蓝牙芯片模块市场的百花齐放&#xff0c;也带来的工程师在选型时碰到很大的困难&#xff0c;但是无论是做半成品&#xff0c;还是做成品&#xf…...

vue 拿到数据后,没有重新渲染视图,nuxt.js拿到数据后,没有重新渲染视图,强制更新视图

以下为Vue2的解决方案 一、 Vue.set&#xff08;&#xff09; 问&#xff1a;什么情况下使用&#xff1f; 答&#xff1a;如果你向响应式数据添加新的“属性”&#xff0c;理论上&#xff0c;一般情况下是没问题的&#xff0c;但是&#xff0c;如果你的级别比较深&#xff0c;又…...

Docker基础操作命令演示

Docker中的常见命令&#xff0c;可以参考官方文档&#xff1a;https://docs.docker.com/engine/reference/commandline/cli/ 1、常见命令介绍 其中&#xff0c;比较常见的命令有&#xff1a; 命令说明文档地址docker pull拉取镜像docker pulldocker push推送镜像到DockerReg…...

XTU-OJ 1175-Change

题目描述 一个班有N个学生&#xff0c;每个学生有第一学期成绩Xi,第二学期成绩Yi&#xff0c;请问成绩上升&#xff0c;持平&#xff0c;下降的人数。 输入 每个样例的第一行是整数N(0≤N≤50),如果N0&#xff0c;表示输入结束&#xff0c;这个样例不需要处理。 第二行是N个整数…...

Python环境安装

环境安装 Windows安装Linux安装 Windows安装 下载最新版Python https://www.python.org/downloads 打开安装包 选择安装路径&#xff0c;安装 安装 验证安装是否成功&#xff0c;命令行输入python Linux安装 安装依赖环境 yum install wget zlib-devel bzip2-devel op…...

苏轼在密州的四首千古名作

苏轼&#xff0c;一个从诗歌王国掉落人间的落魄贵族&#xff0c;整个政治生涯几乎都以流浪为主&#xff0c;在古诗词世界或许只有李白与之最是相似&#xff0c;不过李白的流浪属于荡歌山水、云游四方&#xff0c;而苏轼的流浪则带有被动的成分&#xff1a;一纸贬黜公文就是苏轼…...

[计算机提升] 域及域用户(组)

1.3 域及域用户(组) 1.3.1 域的概念 在Windows操作系统中&#xff0c;域&#xff08;Domain&#xff09;是指一个或多个计算机或资源的集合&#xff0c;这些计算机或资源受到单个安全数据库&#xff08;域控制器&#xff09;的管理和控制。域可以包含多个计算机、用户、组和其…...

命令行配置文件

在说具体的配置方式之前&#xff0c;我们需要首先梳理清除几个概念。这有助于我们明白自己在做什么&#xff0c;以及如何把经验平移到其他方面。 和命令行相关的有几个概念&#xff1a;terminal&#xff08;终端&#xff09;、shell&#xff08;解释器&#xff09;&#xff1b…...

MPP产品介绍-定位-应用场景-技术特点

产品定位 FusionInsight LibrA是企业级的大规模并行处理关系型数据库。FusionInsight LibrA采用MPP(Massive Parallel Processing)架构&#xff0c;支持行存储与列存储&#xff0c;提供PB(Petabyte&#xff0c;2的50次方字节)级别数据量的处理能力。 FusionInsight LibrA在核…...

Linux性能优化--性能工具:磁盘I/O

6.0 概述 本章介绍的性能工具能帮助你评估磁盘I/O子系统的使用情况。这些工具可以展示哪些磁盘或分区已被使用&#xff0c;每个磁盘处理了多少I/O,发给这些磁盘的I/O请求要等多久才被处理。 阅读本章后&#xff0c;你将能够&#xff1a; 确定系统内磁盘I/O的总量和类型(读/写…...

Archive Team: The Twitter Stream Grab

该集合不再更新&#xff0c;应被视为静态数据集。 从一般 Twitter 流中抓取的 JSON 的简单集合&#xff0c;用于研究、历史、测试和记忆的目的。这是“Spritzer”版本&#xff0c;最轻、最浅的 Twitter 抓取。不幸的是&#xff0c;我们目前无法访问流的洒水器或花园软管版本。 …...

Vue-props配置功能

Vue-props配置功能 props概述 功能&#xff1a;接收从其他组件传过来的数据&#xff0c;将数据从静态转为动态注意&#xff1a; 同一层组件不能使用props&#xff0c;必须是父组件传子组件的形式。父组件传数据&#xff0c;子组件接收数据。不能什么数据都接收&#xff0c;可…...

iMazing 3中文版功能介绍免费下载安装教程

iMazing 3中文版免费下载是一款兼容Win和Mac的iOS设备管理软件。iMazing 3能够将音乐、文件、消息和应用等数据从任何 iPhone、iPad 或 iPod 传输到 Mac 或 PC 上。 使用iMazing 3独特的 iOS 备份功能保证数据安全:设定自动无线备份时间并支持快照;将备份保存到外接驱动器和网…...

给课题组师弟师妹的开荒手册(终篇)

0 写在前面 终于&#xff0c;在结束收尾工作后敲下了开荒手册的终篇&#xff0c;自己三年研究生生活过的离理想中的完美还差很多&#xff0c;不过胜在完整&#xff0c;哈哈&#xff0c;小满胜万全嘛。希望以自己不太完美的经历为例&#xff0c;抛我的砖&#xff0c;引师弟师妹…...

【Eclipse】安装与卸载教程

目录 1.绿色版免安装版本 2.安装版本 3.卸载 首先打开官网&#xff1a;Eclipse Downloads | The Eclipse Foundation 选择download package 如图所示&#xff0c;到如下界面 1.绿色版免安装版本 按图片点击&#xff0c;即可开始下载 下载好后解压 &#xff0c;在桌面创建…...

WordPress还是Shopify?如何选择最适合您业务的网站建设平台?

在当今数字化世界中&#xff0c;创建一个强大的在线存在对于任何业务来说都至关重要。然而&#xff0c;对于不熟悉编码或网站建设的人来说&#xff0c;选择合适的网站建设平台可能是一项挑战。在这个领域&#xff0c;两个主要的选项备受关注&#xff1a;WordPress和Shopify。让…...

Java Kids-百倍提速【Mac IOS】

引言&#xff1a;当今社会&#xff0c;创新和提升效率已经成为了大家普遍的追求。无论是个人生活还是企业经营&#xff0c;我们都希望能够以更高的效率完成任务&#xff0c;节省时间和资源。因此&#xff0c;提速成为了一种时代的要求&#xff0c;而"Java Kids 百倍提速&q…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...