浅谈Spring @Order注解的使用

本文详细解析了Spring框架中@Order注解和Ordered接口的作用,它们用于定义Bean的执行顺序而非加载顺序,通过实例展示了如何调整执行优先级,并深入探讨了Spring容器加载组件的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响;

1.@Order的注解源码解读
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {

	/**
	 * 默认是最低优先级,值越小优先级越高
	 */
	int value() default Ordered.LOWEST_PRECEDENCE;

}
  • 注解可以作用在类(接口、枚举)、方法、字段声明(包括枚举常量);
  • 注解有一个int类型的参数,可以不传,默认是最低优先级;
  • 通过常量类的值我们可以推测参数值越小优先级越高;
2.Ordered接口类
package org.springframework.core;

public interface Ordered {
    int HIGHEST_PRECEDENCE = -2147483648;
    int LOWEST_PRECEDENCE = 2147483647;

    int getOrder();
}
3.创建BlackPersion、YellowPersion类,这两个类都实现CommandLineRunner

实现CommandLineRunner接口的类会在Spring IOC容器加载完毕后执行,适合预加载类及其它资源;也可以使用ApplicationRunner,使用方法及效果是一样的

package com.yaomy.common.order;

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Description: Description
 * @ProjectName: spring-parent
 * @Version: 1.0
 */
@Component
@Order(1)
public class BlackPersion implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("----BlackPersion----");
    }
}
package com.yaomy.common.order;

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Description: Description
 * @ProjectName: spring-parent
 * @Version: 1.0
 */
@Component
@Order(0)
public class YellowPersion implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("----YellowPersion----");
    }
}
4.启动应用程序打印出结果
----YellowPersion----
----BlackPersion----

我们可以通过调整@Order的值来调整类执行顺序的优先级,即执行的先后;当然也可以将@Order注解更换为Ordered接口,效果是一样的

5.到这里可能会疑惑IOC容器是如何根据优先级值来先后执行程序的,那接下来看容器是如何加载component的
  • 看如下的启动main方法
@SpringBootApplication
public class CommonBootStrap {
    public static void main(String[] args) {
        SpringApplication.run(CommonBootStrap.class, args);
    }
}

这个不用过多的解释,进入run方法…


    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            //这里是重点,调用具体的执行方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
   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);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();
        //循环调用具体方法
        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }

    }

    private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
        try {
            //执行方法
            runner.run(args);
        } catch (Exception var4) {
            throw new IllegalStateException("Failed to execute ApplicationRunner", var4);
        }
    }

    private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
        try {
            //执行方法
            runner.run(args.getSourceArgs());
        } catch (Exception var4) {
            throw new IllegalStateException("Failed to execute CommandLineRunner", var4);
        }
    }

到这里优先级类的示例及其执行原理都分析完毕;不过还是要强调下@Order、Ordered不影响类的加载顺序而是影响Bean加载如IOC容器之后执行的顺序(优先级);

个人理解是加载代码的底层要支持优先级执行程序,否则即使配置上Ordered、@Order也是不起任何作用的,
个人的力量总是很微小的,欢迎大家来讨论,一起努力成长!!

GitHub源码:https://github.com/mingyang66/spring-parent

<think>我们正在讨论的是Spring框架中的@Order注解,而不是之前提到的toString()方法。根据用户的问题和提供的引用,用户希望了解@Order注解使用方法及其在Spring框架中的作用。下面我将结合引用内容,详细解释@Order注解。 引用[1]提到:@Order注解是控制组件执行顺序的核心工具,影响Bean在集合中的注入顺序、切面的执行顺序以及扩展点(如过滤器、监听器)的排序。 引用[2]和[3]也强调了@Order注解用于控制Bean的加载顺序,特别是在多个Bean实现同一接口时,可以指定优先级。 下面我将从使用教程、原理分析和常见场景三个维度展开: ### 一、@Order注解基础 1. **作用**:在Spring框架中,`@Order`注解用于定义组件的加载或执行顺序。数值越小,优先级越高(即数值小的先加载或先执行)。 2. **应用范围**: - 控制多个`Bean`的加载顺序(例如,同一接口的多个实现类,在注入到List时,会按照`@Order`顺序排列)。 - 控制切面(AOP)的执行顺序。 - 控制过滤器(Filter)、监听器(Listener)等Web组件的执行顺序。 ### 二、使用方法 1. **基本语法**: ```java @Order(1) // 数字表示优先级,可以是任意整数(包括负数) ``` 2. **使用位置**: - 类级别:通常用于`@Component`及其衍生注解(如`@Service`, `@Repository`)标注的类。 - 方法级别:用于`@Bean`标注的方法(在配置类中)。 - 切面类:用于`@Aspect`标注的类,控制通知的执行顺序。 3. **示例代码**: - **场景1**:多个Bean实现同一接口,按顺序注入到List。 ```java public interface MyService { void doSomething(); } @Service @Order(2) public class ServiceA implements MyService { // 实现方法 } @Service @Order(1) public class ServiceB implements MyService { // 实现方法 } ``` 在另一个Bean中注入: ```java @Autowired private List<MyService> services; // 顺序为ServiceB, ServiceA ``` - **场景2**:在配置类中使用`@Order`控制Bean的加载顺序(引用[4]示例): ```java @Configuration public class OrderConfig { @Bean @Order(1) public String firstBean() { return "First"; } @Bean @Order(1) // 相同顺序值,则按方法名(或类名)的字母顺序加载 public String secondBean() { return "Second"; } } ``` 注意:当两个Bean的`@Order`值相同时,Spring会按Bean名称(方法名或类名)的字母顺序加载(引用[4])。 - **场景3**:控制切面顺序 ```java @Aspect @Component @Order(1) public class LoggingAspect { // 通知定义 } @Aspect @Component @Order(2) public class TransactionAspect { // 通知定义 } ``` 此时,`LoggingAspect`的通知会先于`TransactionAspect`执行(因为1<2)。 ### 三、原理分析 - **底层机制**:Spring通过`Ordered`接口(其中定义了`int getOrder()`方法)来实现排序。`@Order`注解会被解析为`Ordered`接口的实现。 - 在注入集合(如List)时,Spring会收集所有匹配类型的Bean,然后根据它们的顺序值(通过`Ordered`接口或`@Order`注解获取)进行排序。 - 对于相同顺序值的情况,Spring会按Bean的名称(字母顺序)排序(引用[4])。 ### 四、常见问题与注意事项 1. **相同顺序值**:当多个Bean的`@Order`值相同时,Spring会按Bean名称(字母顺序)决定顺序(引用[4])。 2. **默认顺序**:若未指定`@Order`,则默认值为`Ordered.LOWEST_PRECEDENCE`(即最低优先级,值为`Integer.MAX_VALUE`)。 3. **负数的使用**:可以使用负数表示高优先级(例如`@Order(-1)`比`@Order(0)`优先级更高)。 ### 五、总结 `@Order`注解Spring框架中用于控制组件顺序的重要工具,通过指定优先级数值来影响Bean的加载顺序、切面执行顺序等。正确使用`@Order`可以解决多Bean依赖、切面顺序冲突等问题。 §§相关问题§§ 1. 在Spring Boot中如何自定义过滤器的执行顺序? 2. 如何在没有`@Order`注解的情况下控制Bean的加载顺序? 3. `@Order`和`@Priority`注解有什么区别?</think>### Spring框架中@Order注解详解 @Order注解Spring框架中用于控制**组件执行顺序**的核心工具,通过定义优先级影响Bean的加载、注入和执行顺序。以下是详细解析: --- #### 一、核心作用 1. **控制Bean加载顺序** 当多个Bean实现同一接口时,通过`@Order`指定加载优先级,数值越小优先级越高[^2][^3]。 ```java @Bean @Order(1) // 优先加载 public Service highPriorityService() { return new ServiceImplA(); } @Bean @Order(2) public Service lowPriorityService() { return new ServiceImplB(); } ``` 2. **影响集合注入顺序** 注入`List<Interface>`时,元素按`@Order`值排序[^1]: ```java @Autowired private List<MyService> services; // 顺序: [@Order(1)的Bean, @Order(2)的Bean...] ``` 3. **控制切面(AOP)执行顺序** 指定多个切面的执行优先级[^1]: ```java @Aspect @Component @Order(1) // 先执行 public class LoggingAspect { ... } @Aspect @Component @Order(2) // 后执行 public class TransactionAspect { ... } ``` --- #### 二、使用方法 ##### 1. 基本语法 ```java @Order(优先级数值) // 数值范围: Integer.MIN_VALUE ~ Integer.MAX_VALUE ``` - **数值越小,优先级越高**(如`@Order(1)`优先于`@Order(2)`) - 默认值:`Ordered.LOWEST_PRECEDENCE`(`Integer.MAX_VALUE`) ##### 2. 使用位置 | 应用场景 | 示例 | |------------------|-------------------------------| | 类级别 | `@Service @Order(1) class A` | | `@Bean`方法 | `@Bean @Order(2) BeanType b` | | 切面类 | `@Aspect @Order(3) class C` | | 过滤器/监听器 | `FilterRegistrationBean` | ##### 3. 相同优先级的处理 当多个Bean的`@Order`值相同时: - Spring按**Bean名称字母顺序**加载[^4] - 示例:`firstBean`优先于`secondBean` ```java @Bean @Order(1) public A firstBean() {...} // 先加载 @Bean @Order(1) public B secondBean() {...} // 后加载 ``` --- #### 三、典型应用场景 1. **依赖注入顺序控制** 确保基础服务先于业务服务初始化: ```java @Configuration public class ServiceConfig { @Bean @Order(1) public DatabaseService databaseService() {...} // 先初始化 @Bean @Order(2) public BusinessService businessService() {...} // 依赖databaseService } ``` 2. **Web过滤器链顺序** 控制过滤器的执行顺序: ```java @Bean @Order(1) // 最先执行 public FilterRegistrationBean<AuthFilter> authFilter() { return new FilterRegistrationBean<>(new AuthFilter()); } @Bean @Order(2) // 其次执行 public FilterRegistrationBean<LoggingFilter> loggingFilter() { return new FilterRegistrationBean<>(new LoggingFilter()); } ``` 3. **事件监听器顺序** 指定事件监听器的触发顺序: ```java @Component @Order(1) public class FirstListener implements ApplicationListener<Event> {...} @Component @Order(2) public class SecondListener implements ApplicationListener<Event> {...} ``` --- #### 四、底层原理 1. Spring通过`OrderComparator`类实现排序逻辑 2. 排序时检查以下接口/注解: - `Ordered`接口(返回`int`值) - `@Order`注解 - `@Priority`注解(JSR-250标准) 3. 执行优先级:`Ordered.getOrder() > @Order > @Priority` --- #### 五、最佳实践 1. **显式声明顺序** 对关键组件始终显式设置`@Order`值,避免依赖默认行为 2. **预留顺序区间** 按功能模块划分顺序区间,便于扩展: ```java public interface OrderGroups { int CORE_SERVICE = 100; int BUSINESS_SERVICE = 200; } @Service @Order(OrderGroups.CORE_SERVICE) public class CoreService {...} ``` 3. **避免过度使用** 仅在顺序敏感的场景(如初始化依赖、切面链)中使用 > **注意**:`@Order`仅影响Spring容器管理的Bean顺序,不影响JVM类加载顺序
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值