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

这篇具有很好参考价值的文章主要介绍了Spring(18) @Order注解介绍、使用、底层原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简介

@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 注入使用示例

包结构如下:

@order,Spring,spring,java,后端

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,Spring,spring,java,后端

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

执行结果如下:

@order,Spring,spring,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,Spring,spring,java,后端

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

执行结果如下:

@order,Spring,spring,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,但是并没有生效。

@order,Spring,spring,java,后端

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 排序调用图

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

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文章来源地址https://www.toymoban.com/news/detail-829379.html

到了这里,关于Spring(18) @Order注解介绍、使用、底层原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL 系列:注意 ORDER 和 LIMIT 联合使用的陷阱

    不知道大家在在分页查询中有没有遇到过这个问题,分页查询中不同的页中出现了同一条数据,出现了分页错乱的问题: 整体排序: SELECT * from test_1 ORDER BY create_date; 提取排序后的前两条: SELECT * from test_1 ORDER BY create_date LIMIT 0,2; 提取排序后的最后两条: SELECT * from test_1 ORDE

    2024年02月04日
    浏览(33)
  • MySQL使用SELECT 语句不加ORDER BY默认是如何排序的?

    大家好,我是阿飞云 怕什么真理无穷,进一步有近一步的欢喜 记录一个 MySQL 查询排序的问题,一个SQL语句没有加 order by ,那么查询出来的结果到底是按照什么规则排序的呢?查询了网上的一些资料,分享如下: •MyISAM 表 MySQL Select 默认排序是按照物理存储顺序显示的(不

    2024年02月10日
    浏览(37)
  • 如何使用SQL系列 之 如何在SQL中使用GROUP BY和ORDER BY

    结构化查询语言(SQL)数据库可以跨多个表存储和管理大量数据。对于大型数据集,理解如何排序数据是很重要的,特别是对于分析结果集或为报告或外部通信组织数据。 SQL中有两个常用的用于数据排序的语句: GROUP BY 和 ORDER BY 。 GROUP BY 语句根据查询中指定的列对数据进行分组

    2024年02月09日
    浏览(42)
  • Mysql 中,为什么 WHERE 使用别名会报错,而 ORDER BY 不会报错?

       我们先对salary * 12 命名一个别名annual_sal  这段代码以annual_sal升序输出且正常执行没有报错。说明 order by 可以使用别名  我们再看看这个段代码 这段代码就报错了,报错说明是 Unknown column \\\'annual_sal\\\' in \\\'where clause\\\'。 由此可以得出where语句执行是找不到annual_sal. 要解决这个问

    2023年04月15日
    浏览(35)
  • MySQL 数据库查询与数据操作:使用 ORDER BY 排序和 DELETE 删除记录

    使用 ORDER BY 语句按升序或降序对结果进行排序。 ORDER BY 默认按升序排序。要按降序排序结果,使用 DESC 。 示例按名称按字母顺序排序结果: ORDER BY DESC 使用 DESC 以降序排序结果。 示例按名称以字母逆序排序结果: 您可以使用\\\"DELETE FROM\\\"语句从现有表格中

    2024年02月05日
    浏览(63)
  • vue路径component使用import的方式来写 component: () => import(‘@/views/order/orderDetail‘)

    以前老师教的一直是这种写法:  工作后突然看到这种写法,我蒙了  首先我们先来理解一下,路由文件里面. path里面其实就是url也就是我们写路径的,我们在网址栏所看到的. 而component则是我们所对应的页面组件.每个页面相当于一个组件.所以我们的页面的名字就是组件的名字.

    2023年04月08日
    浏览(66)
  • mysql中order by多个字段 order by字段可以为空吗

    在MySQL中,要使用“ORDER BY”语句来进行多字段排序,必须先将多个字段的名称按照顺序排列放在“ORDER BY”后面,然后按照每个字段单独的排序规则进行排序。 排序字段的顺序按照order by语句中的先后顺序进行, 先根据第一个排序字段排序 ,如果有相同的值,则根据第二个

    2024年02月03日
    浏览(29)
  • Mybatis ORDER BY 排序失效 & ORDER BY 与 CASE WHEN THEN 排序问题

    如果传递给 mapper 的参数值是以 #{test_参数} 的形式,那么就会报错 具体如下: 传递参数是 name 排序规则是升序 asc ORDER BY 后 使用 #{ } 获取参数值,运行后,会报错的,必须改成 ${ } ,井号改成 美元符号。 如下所示: 数据库表 test_table 的真实字段名: test_id 测试参数值:

    2024年02月11日
    浏览(41)
  • volatile关键字原理的使用介绍和底层原理解析和使用实例

    volatile 的主要作用是保证可见性和有序性,禁止编译器优化。 保证可见性:当一个变量被声明为 volatile 之后,每次读取这个变量的值都会从主内存中读取,而不是从缓存中读取,这就保证了不同线程对这个变量操作的可见性。 有序性:volatile 保证了不同线程对一个 vo

    2024年02月02日
    浏览(27)
  • 你对Spring Security使用场景以及底层原理有了解吗?

    Spring Security是一个基于Spring框架的安全性解决方案,提供了全面的身份验证、授权和安全功能。它可以应用于多种场景以确保应用程序的安全性和保护敏感资源。以下是一些常见的Spring Security的使用场景: 用户登录和认证:Spring Security可以处理用户的身份验证,包括用户名密

    2024年02月05日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包