bean 的 加载顺序

前言

作为一名面向Spring开发的开发人员,在日常的工作、学习或者面试中或多或少都会遇到这样一个问题:如何控制Bean的加载顺序?

在开始回答这个问题之前我们需要先解答另一个问题:为什么需要进行Bean的加载顺序控制?
Spring框架的开发中,常常会面临以下情境:某个Bean依赖于另一个Bean,也就是说,要创建Bean B,必须首先创建Bean A。在这里插入图片描述

正常情况下,Spring 容器加载 Bean 的顺序是不确定的,那么我们如果需要按顺序加载 Bean 时应如何操作?本文将详细讲述我们如何才能控制 Bean 的加载顺序。

两个误区

  • 在被标注了@Configuration的类中,写在前面的@Bean方法并不一定会先被注册。
    这个观点在Spring的早期版本中也不成立,因为在XML配置时,Spring并不是逐行逐个注册Bean的,而是先解析整个XML文件,然后进行依赖分析和注册。尽管在Spring Boot中省略了XML配置,直接将配置类解析成Spring内部对象,但加载方式并没有发生根本性改变。

  • 使用@Order注解并不能控制所有Bean的加载顺序。
    实际上,将@Order注解应用于普通方法或类上是无效的。@Order注解能够影响加载顺序的Bean有一定的限制。
    最开始@Order注解用于切面的优先级指定;在 4.0 之后对它的功能进行了增强,支持集合的注入时,指定集合中bean的顺序,并且特别指出了,它对于但实例的 bean 之间的顺序,没有任何影响。

1、使用@Order注解

@Order注解是Spring框架中的一个注解,位于spring-core包下。它的作用是 定义Spring IOC容器中Bean的执行顺序的优先级,间接性地决定它们在容器中被加载的先后顺序。

@Order注解可以用于类、方法和字段上。
当用于类上时,它指定了该类在容器中的加载顺序。当用于方法或字段上时,它定义了与其他Bean对象之间的执行顺序关系。
具体的优先级数值越,优先级越

1.1 使用示例

我们准备两个类,Demo1和Demo2,分别在类上添加@Order注解:

@Component@Order(1)
public class Demo1 {    
@Bean    
public UserService serviceA(){
        System.out.println("serviceA 执行");
        return new UserService();    
}}

两个类各自创建一个UserService的bean

@Component
@Order(2)
public class Demo2 {
    @Bean    
    public UserService serviceB(){
            System.out.println("serviceB 执行");        
            return new UserService();    
    }
}

运行下面的代码,可以看到,order数字更小的demo1这个类的bean比demo2的bean要先创建出来。

1.2 @order失效问题解决办法

1、在@Configuration里面通过@Bean是方式创建bean,在上面加@Order控制顺序是没有效果的。

2、控制顺序方式:

  • 实现Ordered接口

  • 实现PriorityOrdered接口

  • 在类上面加@Order

  • 在类上面加@Priority

  • 可以通过RegistrationBean方式创建bean,用setOrder添加顺序

  • filter可以通过FilterRegistrationBean创建filter的bean,指定顺序

解决方式1

将需要控制bean的创建顺序的两个类放到不同的包路径下。

再次运行测试代码,当demo2的order数字更小的情况下,就能看到预期的demo2创建的bean优先输出的效果了。

解决方式2

重新修改类的命名,比如将demo2的类修改为ADemo,让ADemo这个类在同一个包路径下置于Demo2之前。

这样修改后,再次运行测试代码观察效果,此时ADemo就优先Demo1了。

1.3实现Ordered接口

如果您想使用Spring提供的Ordered接口来定义类的执行顺序,只需自定义的类实现该接口,并重写getOrder方法。在getOrder方法中,返回的数值越小,优先级就越高。以下是一个示例,展示了如何自定义两个由Spring管理的类,并实现了Ordered接口:

@Componentpublic 
class Listener1 implements Ordered {     
	@Bean    
	public UserService userService1(){        
		System.out.println("userService1 bean");        
		return new UserService();   
	}     
	@Override    
	public int getOrder() {        
		return 100;    
	}
}
@Componentpublic 
class Listener2 implements Ordered {
     @Bean    
     public UserService userService2(){
             System.out.println("userService2 bean");        
             return new UserService();
     }     
     @Override    
     public int getOrder() {
             return 10;    
     }
}

2 使用@DependsOn注解

@DependsOn注解可以用来控制bean的创建顺序,该注解用于声明当前bean依赖于另外一个bean。所依赖的bean会被容器确保在当前bean实例化之前被实例化。

示例:

@Configurationpublic
class BeanOrderConfiguration {
    @Bean
    @DependsOn("beanB")
    public BeanA beanA() {
        System.out.println("bean A init");
        return new BeanA();
    }

    @Bean
    public BeanB beanB() {
        System.out.println("bean B init");
        return new BeanB();
    }

    @Bean
    @DependsOn({"beanD", "beanE"})
    public BeanC beanC() {
        System.out.println("bean C init");
        return new BeanC();
    }

    @Bean
    @DependsOn("beanE")
    public BeanD beanD() {
        System.out.println("bean D init");
        return new BeanD();
    }

    @Bean
    public BeanE beanE() {
        System.out.println("bean E init");
        return new BeanE();
    }
}

以上代码bean的加载顺序为:

bean B init
bean A init
bean E init
bean D init
bean C init

使用@DependsOn注解有以下几种情况:

直接或者间接标注在带有@Component注解的类上。

直接或者间接标注在带有@Bean注解的方法上。

需要注意的是,@DependsOn注解在类级别上仅在使用组件扫描(component-scanning)方式时有效。如果带有@DependsOn注解的类是通过XML方式进行配置,那么该注解会被忽略。在这种情况下,可以通过XML配置中的来实现依赖控制。

3 参数注入

在使用@Bean标注的方法时,如果您传入了参数,Spring Boot会自动在Spring上下文中查找具有相同类型的引用,并首先初始化该类的实例。这一特性也可以用来控制Bean的加载顺序。

示例:

    @Beanpublic
    BeanA beanA(BeanB demoB) {
        System.out.println("bean A init");
        return new BeanA();
    }

    @Beanpublic
    BeanB beanB() {
        System.out.println("bean B init");
        return new BeanB();
    }

以上结果,beanB先于beanA被初始化加载。

需要注意的是,Springboot会按类型去寻找。如果这个类型有多个实例被注册到Spring上下文,那你就需要加上@Qualifier("Bean的名称")来指定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值