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

news2024/11/24 4:33:57

目录

    • 一、简介
    • 二、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() ==");
    }

    @Override
    public 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() ===");
    }

    @Override
    public 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);
    }

    @Bean
    public 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 {

    @Override
    public 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 {

    @Override
    public 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)
    @Bean
    public CommandLineRunner commandLineRunner01() {
        return args -> System.out.println("=== commandLineRunner01 ===");
    }

    @Order(1)
    @Bean
    public 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1101625.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

css 双栏布局 左侧宽度自适应 右侧自适应

如标题所述&#xff0c;为了实现左侧宽度自适应&#xff0c;右侧使用剩余空间。 第一种方案&#xff1a;使用table实现 <table classNamewrap><tbody><tr><td className"left1">leftleft</td><td className"right1">…

竞赛 深度学习YOLO安检管制物品识别与检测 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络4 Yolov55 模型训练6 实现效果7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习YOLO安检管制误判识别与检测 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&…

排序算法可视化

前言 这两天在B站上刷到一个视频&#xff0c;用python把各种排序动画可视化显示了出来觉得还蛮好玩的&#xff0c;当即就决定用Flutter写一个玩玩&#xff0c;顺便复习一下排序算法&#xff0c;话不多说&#xff0c;进入正文~ 效果图&#xff1a; 该效果图为鸡尾酒排序(双向冒…

【Machine Learning】03-Unsupervised learning

03-Unsupervised learning 3. Unsupervised Learning3.1 无监督学习&#xff08;Unsupervised Learning&#xff09;3.1.1 聚类&#xff08;Clustering&#xff09;3.1.2 K-均值聚类算法&#xff08;K-means Clustering&#xff09;3.1.3 高斯分布&#xff08;Gaussian distrib…

当然是做药物研发啦~~~

做药物研发 梦开始的地方 当年上本科的时候&#xff0c;我的第一台笔记本有点子差&#xff0c;根本跑不了分子动力学&#xff0c;而且还需要我这个菜鸡自己在win系统的本子上安装Linux的双系统&#xff0c;频繁的重启、安装双系统搞得我心力憔悴&#xff0c;于是我决定使用”钞…

使用navicat查看类型颜色

问题描述&#xff1a; 最近遇到一个mongodb的数据问题。 在date日期数据中&#xff0c;混入了string类型的数据&#xff0c;导致查询视图报错&#xff1a; $add only supports numeric or date types解决办法&#xff1a; 使用类型颜色工具。 找到在last_modified_date字段中…

springBoot复杂对象表示和lombok的使用

springBoot复杂对象表示 前言简单案例lombok的使用通过properties文件进行绑定在yaml文件中使用 前言 对象&#xff1a;键值对的集合&#xff0c;如&#xff1a;映射&#xff08;map)/哈希&#xff08;hash)/字典&#xff08;dictionary&#xff09; 数组&#xff1a;一组按次…

亚马逊测评关于IP和DNS的问题

最近不少人询问了关于IP和DNS的问题&#xff0c;在此进行一些科普。 当客户端试图访问一个网站时&#xff0c;首先会向其所在的ISP的DNS服务器进行查询。如果ISP的DNS服务器没有相关缓存&#xff0c;则会向上级DNS服务器进行查询。 一些诸如CDN之类的服务&#xff0c;可能会为…

【C/C++】STL——容器适配器:stack和queue的使用及模拟实现

​&#x1f47b;内容专栏&#xff1a; C/C编程 &#x1f428;本文概括&#xff1a;stack与queue的介绍与使用、模拟实现。 &#x1f43c;本文作者&#xff1a; 阿四啊 &#x1f438;发布时间&#xff1a;2023.10.17 一、stack的介绍与使用 1.1 stack的介绍 以下是stack的文档…

E044-服务漏洞利用及加固-利用redis未授权访问漏洞进行提权

任务实施: E044-服务漏洞利用及加固-利用redis未授权访问漏洞进行提权 任务环境说明&#xff1a; 服务器场景&#xff1a;p9_kali-6&#xff08;用户名&#xff1a;root&#xff1b;密码&#xff1a;toor&#xff09; 服务器场景操作系统&#xff1a;Kali Linux 192.168.3…

【Qt高阶】Linux安装了多个版本的Qt 部署Qt程序,出包【2023.10.17】

简介 linux系统下可执行程序运行时会加载一些动态库so&#xff0c;有一些是Qt的库&#xff0c;Qt的库会加载其他更基础的库。最后出包的时候需要把依赖的包整理到一个文件夹&#xff0c;来制作安装包。近期遇到已经将依赖的so文件拷贝至程序目录下&#xff0c;但还是调系统路径…

AutoCAD 2024:计算机辅助设计(CAD)软件中文版

AutoCAD是一款广受全球设计师和工程师欢迎的计算机辅助设计&#xff08;CAD&#xff09;软件。自1982年首次推出以来&#xff0c;AutoCAD已经经历了多次迭代和改进&#xff0c;不断提升用户在产品设计、建造和工程领域的工作效率。现在&#xff0c;让我们一起探索AutoCAD 2024的…

MySQL中的存储过程

MySQL中的存储过程 概述 由MySQL5.0 版本开始支持存储过程。 如果在实现用户的某些需求时&#xff0c;需要编写一组复杂的SQL语句才能实现的时候&#xff0c;那么我们就可以将这组复杂的SQL语句集提前编写在数据库中&#xff0c;由JDBC调用来执行这组SQL语句。把编写在数据库…

横向移动如何阻止以及防范?

文章目录 背景总结EDR设备监测 (这里以奇安信网神云锁为例) 背景 今天面试&#xff0c;面试官问到了这一个问题&#xff0c;云主机被getshell了&#xff0c;进行了横向移动&#xff0c;如何进行阻止以及防范&#xff1f;当时回答了两个点&#xff1a;通过防火墙出入站策略设置…

HTX 与 Zebec Protocol 展开深度合作,并将以质押者的身份参与 ZBC Staking

自2023年下半年以来&#xff0c;加密市场始终处于低迷的状态&#xff0c;在刚刚结束的9月&#xff0c;加密行业总融资额创下2021年以来的新低&#xff0c;同时在DeFi领域DEX交易额为318.9亿美元&#xff0c;同样创下2021年1月以来的新低。 对于投资者而言&#xff0c;难以从外生…

Apache DolphinScheduler 3.0.0 升级到 3.1.8 教程

安装部署可参考官网 Version 3.1.8/部署指南/伪集群部署(Pseudo-Cluster)https://dolphinscheduler.apache.org/zh-cn/docs/3.1.8/guide/installation/pseudo-cluster 也可以参考我写贴子 DolphinScheduler 3.0安装及使用-CSDN博客DolphinScheduler 3.0版本的安装教程https:…

微信查分,原来这么简单,老师必看攻略

哈喽&#xff0c;亲爱的老师们&#xff01;是不是经常为了查找学生的成绩而烦恼呢&#xff1f;别担心&#xff0c;今天我就来给大家分享一个超级实用的教程——在微信里查分&#xff01;快来一起了解一下吧&#xff01; 首先&#xff0c;我们要清楚成绩查询页面是什么。一般来说…

成都优优聚是专业美团代运营吗?

成都优优聚是一家专注于美团代运营的公司。作为全国知名的美团代运营服务商&#xff0c;成都优优聚拥有丰富的经验和优秀的团队&#xff0c;为各类商家提供全方位的美团代运营解决方案。 美团作为目前国内最大的O2O平台之一&#xff0c;拥有庞大的用户基础和强大的品牌影响力。…

激光雷达标定板精准识别前方障碍物

商用车自动驾驶率先进入商业化运营阶段&#xff0c;这主要是由于商用车对价格的敏感度更低、B端付费意愿更高&#xff0c;以及场景交通复杂程度较低和政策鼓励等因素。在矿区、港口、干线物流、机场、物流园区等细分场景&#xff0c;高级别自动驾驶正在孕育新市场。其中&#x…

【java学习—八】对象类型转换Casting(1)

文章目录 1. 数据类型转换1.1 基本数据类型的 Casting1.2. 对 Java 对象的强制类型转换(造型)2. 对象类型转换举例 1. 数据类型转换 数据类型转换分为基本数据类型转换和对象类型转换。 1.1 基本数据类型的 Casting (1) 自动类型转换&#xff1a;小的数据类型可以自动转换成…