Spring Bean创建顺序与@Order注解

1. Spring Bean 创建顺序

假如不同的 Bean 之间有依赖关系,则完成依赖的 Bean 创建过程之前,需要先完成被依赖的 Bean 创建过程

假如不同的 Bean 之间没有依赖关系,则这些 Bean 之间的创建顺序先后不会产生影响(没有依赖关系的 Bean,谁先创建谁后创建不会影响功能,也不会导致异常)

1.1. Bean 创建的总体顺序

Spring 初始化过程中,对非懒加载的 Bean 及被这些 Bean 依赖的 Bean 进行创建,完成所有 Bean 的创建之后,再触发 ContextRefreshedEvent 事件,总体顺序如下:

创建 Bean 实例
    1.a. 假如 Bean 有使用构造函数注入,则按照以下顺序执行
        1.a.1. 执行相同的步骤创建构造函数注入中被依赖的 Bean
        1.a.2. 调用当前 Bean 的有参数构造函数创建实例
    1.b. 假如 Bean 没有使用构造函数注入,则调用 Bean 的无参数构造函数创建实例
    2. 对 Bean 的字段注入被依赖的 Bean 实例进行创建
        执行相同的步骤创建被依赖的 Bean
    3. 调用 Bean 的 set 方法注入被依赖的 Bean 实例
        执行相同的步骤创建被依赖的 Bean
    4. 调用 Bean 实现 ApplicationContextAware 接口的 setApplicationContext() 方法
    5. 调用 BeanPostProcessor 实现类的 postProcessBeforeInitialization() 方法
    6. 调用 Bean 实现 InitializingBean 接口的 afterPropertiesSet() 方法
    7. 调用 Bean 的初始化方法(XML 中的 init-method、或有 @PostConstruct 注解的方法)
    8. 调用 BeanPostProcessor 实现类的 postProcessAfterInitialization() 方法
(创建所有的 Bean 之后)触发 ContextRefreshedEvent 事件

1.2. 不同方式定义的 Bean 创建顺序

通过以下不同的方式可以定义 Bean:

在 Spring XML 文件中定义 Bean
使用 @Component、@Repository、@Service、@Configuration、@Controller 等注解定义 Bean
在指定了 @Configuration 注解的类的方法上指定 @Bean 注解定义 Bean

假如 Bean 相互之间有依赖关系,则创建依赖的 Bean 之前会先创建被依赖的 Bean,具体见以上总体顺序。例如 BeanA 依赖了 BeanB,则 Spring 在完成依赖的 BeanA 的创建之前,会先创建被依赖的 BeanB

假如 Bean 之间没有依赖关系,则不同 Bean 的创建顺序与对应类所在的模块(main/test)、包名及类名顺序有关;假如是使用 @Bean 注解定义的 Bean,则与 @Bean 注解所在的方法在类中的顺序有关

假如 Bean 之间没有依赖关系,则不需要特别关注这些 Bean 的创建顺序(没有依赖关系的 Bean 的创建顺序存在以上隐式的创建顺序,不能根据这些隐式的顺序进行需要保证顺序的处理)

1.3. 懒加载

  • 创建时间点

对于非懒加载的 Bean,在 Spring 初始化期间会被创建

对于懒加载的 Bean,当依赖这些 Bean 的其他 Bean 被创建时,这些懒加载的 Bean 会被创建

在 Spring 初始化期间,对于懒加载的 Bean,假如依赖这些 Bean 的其他 Bean 没有被创建,则这些懒加载的 Bean 也不会被创建

  • 默认值及设置

Spring XML 中定义的 Bean,及通过注解定义的 Bean 默认是懒加载

在 Spring XML 中,将 bean 元素的 lazy-init 设置为 “false” 可以指定为非懒加载

使用 @Component 等注解定义的 Bean,可在对应类上添加 @Lazy(false) 注解,指定为非懒加载;使用 @Bean 注解定义的 Bean,可在对应方法上添加 @Lazy(false) 注解,指定为非懒加载

1.4. 构造函数注入

示例如下:

public class XXX {
    public XXX(YYY yyy) {
    }
}

public class XXX {
    public XXX(Map<String, YYY> yyyMap) {
    }
}
  • 依赖的 Bean 与被依赖的 Bean 的创建顺序

使用构造函数注入时,Spring 在创建依赖的 Bean(如以上 XXX)之前,会先完成被依赖的 Bean(如以上 YYY)的创建

在依赖的 Bean 有参数的构造函数(用于注入的构造函数)被调用之前,被依赖的 Bean 的完整的创建过程都已经完成

  • 构造函数不同参数类型

使用构造函数注入时,构造函数的参数可以有一个或多个

参数类型可以为 Bean 的类型;或者使用 Map 格式,key 为 Bean 的名称,value 为对应的 Bean 实例

  • 构造函数不同参数类型的区别

假如构造函数的参数类型为 Bean,则对应的 Bean 必须存在且仅存在一个,否则会导致 Spring 初始化失败

假如构造函数的参数类型为 Map,则对应的 Bean 不存在,或存在一个,或存在多个时,Spring 都能正常初始化(对应的 Bean 不存在时,Map 的数量为 0)

  • 构造函数上的 @Autowired 注解

对应的构造函数上可以指定 @Autowired 注解,也可以不指定

1.5. set 注入

示例如下:

public class XXX {
    @Autowired
    public setYYY(YYY yyy) {
        this.yyy = yyy;
    }
}
  • 依赖的 Bean 与被依赖的 Bean 的创建顺序

首先调用依赖 Bean 的构造函数;假如依赖 Bean 存在有参数构造函数则调用有参数构造函数并进行构造函数注入;假如依赖 Bean 不存在有参数构造函数则调用无参数构造函数

再创建被依赖的 Bean,并调用依赖 Bean 的 set 方法注入被依赖的 Bean

  • set 方法上的 @Autowired 注解

对应的 set 方法上的 @Autowired 注解需要指定

  • Spring XML 中 bean.property 元素

假如 Bean 在 Spring XML 中定义,则通过 bean.property 元素定义的字段的赋值与 set 注入顺序类似,晚于 Bean 的构造函数,早于 ApplicationContextAware.setApplicationContext() 方法

1.6. 字段注入

示例如下:

public class XXX {
    @Autowired
    private YYY yyy;
}
  • 依赖的 Bean 与被依赖的 Bean 的创建顺序

首先调用依赖 Bean 的构造函数;假如依赖 Bean 存在有参数构造函数则调用有参数构造函数并进行构造函数注入;假如依赖 Bean 不存在有参数构造函数则调用无参数构造函数

再创建被依赖的 Bean,并将被依赖的 Bean 注入到依赖 Bean 的对应字段

1.7. getBean() 相关方法

org.springframework.context.ApplicationContext 接口(定义在父接口中)的 getBean()、getBeansOfType()、getBeansWithAnnotation() 方法用于获取指定类型、名称或使用指定注解的 Bean,若对应的 Bean 未创建,会创建对应的 Bean

通过以上 getBean() 相关方法触发的 Bean 创建过程,与通过构造函数注入等方式触发的 Bean 创建过程步骤相同

getBeanNamesForType()、getBeanNamesForAnnotation() 用于获取指定类型或有指定注解的 Bean 的名称,不会创建对应的 Bean

1.8. 字段注入的顺序

使用字段注入的方式时,例如 @Autowired、@Resource、@Value、@Inject 等注解,某个类中各个字段被注入的顺序,与字段在类中定义的顺序相同

例如某个类中按照以下方式定义了使用字段注入的字段,各字段在类中定义的顺序是 testBean1、testBean12、testField31、testBean4,则这些字段被注入的顺序也是一样

通常不会依赖 Bean 中字段注入的顺序,不需要特别关注

@Autowired
private TestBean1 testBean1;

@Resource
private TestBean2 testBean12;

@Value
private String testField31;

@Inject
private TestBean13 testBean4;

1.9. @DependsOn 注解

  • 作用及属性

使用 @DependsOn 注解,可以使被依赖的 Bean 在依赖的 Bean 之前创建(被依赖的 Bean 不需要以字段等形式出现在依赖的 Bean 中)

@DependsOn 注解的 value 属性可以指定一个或多个被依赖 Bean 的名称

  • 使用注解定义的 Bean

@DependsOn 注解对通过注解定义的 Bean 有效,对 Spring XML 中定义的 Bean 无效

当依赖的 Bean 通过 @Component 等注解定义时,@DependsOn 注解需要在对应的类上

当依赖的 Bean 使用 @Bean 注解定义时,@DependsOn 注解需要在对应的方法上

在以下示例中,TestBeanA1 依赖了 TestBeanA2,TestBeanA3 依赖了 TestBeanA1

@DependsOn("testBeanA2")
@Service
public class TestBeanA1 {
}

@Service("testBeanA2")
public class TestBeanA2 {
}

@Configuration
public class TestConfiguration1 {
    @DependsOn("testBeanA1")
    @Bean
    public TestBeanA3 getTestBeanA3() {
    }
}
  • Bean 创建顺序

使用 @DependsOn 注解指定 Bean 之间的依赖关系时,Spring 在开始创建依赖的 Bean 之前,会先完成被依赖的 Bean 的创建,顺序如下:

调用被依赖 Bean 的构造函数
完成被依赖 Bean 剩下的创建步骤
调用依赖 Bean 的构造函数
完成依赖 Bean 剩下的创建步骤

1.10. Spring XML 中 bean 元素的 depends-on 属性

对于 Spring XML 中定义的 Bean,需要使用 bean 元素的 depends-on 属性指定被依赖的 Bean 名称,与 @DependsOn 注解效果类似

示例如下:

<bean id="testBean1" class="com.test.TestBean1" depends-on="testBean2"/>

<bean id="testBean2" class="com.test.TestBean2"/>

1.11. BeanPostProcessor 实现类

在实现了 org.springframework.beans.factory.config.BeanPostProcessor 接口的类上指定 @Component 等注解,Spring 会在 Bean 的创建过程中调用这些实现类的方法(类对应的 Bean 不需要指定为非懒加载)

在 Bean 初始化前,BeanPostProcessor 实现类的 postProcessBeforeInitialization() 方法会被调用

在 Bean 初始化后,BeanPostProcessor 实现类的 postProcessAfterInitialization() 方法会被调用

1.12. Spring 事件处理

当 ApplicationContext 完成初始化或刷新时,Spring 会触发 ContextRefreshedEvent 事件

Spring 不会自动触发 ContextStartedEvent 事件,可以调用相关方法触发

为了接收 Spring 的事件并处理,可以通过以下两种方式实现,方法的参数类型应为需要处理的事件类型

1.12.1. 实现 ApplicationListener 接口

在实现了 org.springframework.context.ApplicationListener<E extends ApplicationEvent> 接口的类上指定 @Component 等注解,Spring 会在指定的事件发生时,调用对应的 onApplicationEvent() 方法(类对应的 Bean 不需要指定为非懒加载)

1.12.2. 使用 @EventListener 注解

在指定了 @Component 等注解的类的方法上指定 @EventListener 注解,Spring 会在指定的事件发生时,调用该方法(类对应的 Bean 不需要指定为非懒加载)

2. Spring 的 @Order 注解与 Ordered 接口

2.1. @Order 注解与 Ordered 接口的 order 值

@Order 注解通过 value 属性指定顺序值

Ordered 接口实现类通过 getOrder() 方法返回值指定顺序值

以上的数值越大,优先级越低;数值越小,优先级越高

最高优先级为:

int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

最低优先级为:

int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

@Order 注解的 value 属性值默认为最低优先级,即 LOWEST_PRECEDENCE

2.2. @Order 注解可以控制的顺序

2.2.1. @EventListener 注解方法处理 Spring 事件顺序

在指定了 @EventListener 注解处理 Spring 事件的方法上指定 @Order 注解,可以控制接收对应 Spring 事件的顺序

即处理相同的 Spring 事件时,对应的方法被执行的顺序

@Order 注解的 value 属性值越小,顺序越靠前

2.2.2. ApplicationListener 实现类处理 Spring 事件顺序

在 ApplicationListener 实现类上指定 @Order 注解,可以控制接收对应 Spring 事件的顺序

2.3. Ordered 接口可以控制的顺序

2.3.1. BeanPostProcessor 实现类方法执行顺序

在 BeanPostProcessor 实现类中实现 Ordered 接口,可以控制对应的 postProcessBeforeInitialization()、postProcessAfterInitialization() 方法执行顺序

即处理同一个 Bean 初始化前或初始化后操作时,对应的方法被执行的顺序

Ordered 接口的 getOrder() 方法返回值越小,顺序越靠前

2.3.2. ApplicationListener 实现类处理 Spring 事件顺序

在 ApplicationListener 实现类中实现 Ordered 接口,可以控制接收对应 Spring 事件的顺序

2.4. PriorityOrdered 接口可以控制的顺序

2.4.1. ApplicationListener 实现类处理 Spring 事件顺序

在 ApplicationListener 实现类中实现 PriorityOrdered 接口,可以控制接收对应 Spring 事件的顺序

PriorityOrdered 实现类比任意 Ordered 实现类的优先级都高

2.5. @Order 注解、Ordered 接口都无法控制的顺序

2.5.1. Bean 的创建顺序

Bean 的创建顺序无法通过 @Order 注解、Ordered 接口(包括 PriorityOrdered 接口)控制

@Order 注解可以控制的是 Bean 在注入时的顺序

Bean 的创建顺序需要通过 Bean 之间的依赖关系,或使用 @DependsOn 注解来指定

2.6. @Order 注解无法控制的顺序

2.6.1. BeanPostProcessor 实现类方法执行顺序

BeanPostProcessor 实现类方法执行顺序无法通过 @Order 注解控制

可以通过实现 Ordered 或 PriorityOrdered 接口进行控制

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值