踩坑:@PostConstruct、@DependsOn、@Order注解嵌套使用解决Bean加载优先级问题

简介: 踩坑:@PostConstruct、@DependsOn、@Order注解嵌套使用案例

今天在工作中写需求代码时,遇到的一个关于Sping Bean对象加载优先级问题,结合了Spring源码,大概总结了一下遇到Bean加载顺序需求问题时,比较常用的3个注解:@PostConstruct、@DependsOn、@Order

一、@Order注解的作用

  • @Order注解的作用是定义Spring IOC容器中Bean的执行顺序的优先级。

使用案例:

@Component
@Order(0)
public class Test01 {
   ...
}
@Component
@Order(1)
public class Test02 {
   ...
}
@Component
@Order(2)
public class Test03 {
   ...
}

如上述代码所示,通过@Order注解定义优先级,3个Bean对象从IOC容器中的加载顺序为:Test01、Test02、Test03。


二、@PostConstruct注解的作用

@PostConstruct注解可以用于修饰一个非静态的、返回值类型为 void 的方法(eg:myInit())。

该方法(myInit())会在服务器加载Servlet的时候被执行,且只会被执行一次!

该方法(myInit())的调用执行在构造函数之后,在Servlet的 init() 方法之前,在Servlet的 destroy()方法之后。

使用案例:

@Component
public class Test {
   @PostConstruct
   private void init() {
      // 初始化
      System.out.println("World!");
   } 
   public Test(){
       System.out.println("Hello");
   }
}

输出结果:

Hello
World!

三、@DependsOn注解的作用

该注解的作用顾名思义,就是 “谁依赖谁”。

假如在Test02类上加上@DependsOn(value = "test01"),那么就说明Test02在加载时,要依赖于Test01,Spring IOC 容器会优先加载Test01,然后再加载Test02。

举个实际业务场景的案例,假设现在有2个类Test01、Test02,需要交给Spring IOC容器托管:

/**
 * Test01是一个拥有1个静态变量的类
 */
@Component
public class Test01 {
    // 该静态变量的属性值需要通过Spring容器赋值,值(hello)定义在application.properties中。
    // 注意:@Value注解不可以给静态变量注入属性值 (否则获取的注入结果为null) !
    // 因此HELLO的属性值注入要在setter方法上加上@Value注解,参考文章:【https://blog.csdn.net/weixin_43591980/article/details/121503720】
    public static String HELLO;
    public static String WORLD;
    @Value("${spring.test.hello}")// 值为hello
    public void setHELLO(String hello) {
        HELLO = hello;
    }
    @Value("${spring.test.world}")// 值为world
    public void setWORLD(String world) {
        WORLD = easak;
    }
}

再来看Test02类的代码(前提要求:Test02类需要在我们的Spring Boot项目启动时首先被初始化调用!):

/**
 * Test02拥有一个@PostConstruct注解修饰的init()初始化方法和无参构造函数
 */
@Component
public class Test02 {
    @PostConstruct
    public void init(){
       ...
    }
    public Test02(){
        ... 
    }
}

业务需求:我需要在Test02的无参构造方法加载时,控制台打印Test01类中的HELLO静态变量值,然后在 init()方法执行时,控制台打印Test01类中的WORLD静态变量值。

刚开始我的第一想法是,直接这样写就好了:

@PostConstruct
public void init(){
   System.out.println(Test01.HELLO);
}
public Test02(){
    System.out.println(Test02.WORLD);
}


但是最终控制台打印的结果为:

null
null

Why?为什么是这个结果?

因为,Test02类会在我们的Spring Boot项目启动时首先被初始化调用,也就是说IOC容器会首先去加载Test02对象,而这时候Test01还尚未被加载到容器中,这时候Test01中的两个静态变量HELLO、WORLD还没有通过@Value注解注入属性值,所以结果理所应当是输出 null ~

解决方案:使用@DependsOn注解

我们对Test02类进行改进

@Component
@DependsOn(value = "test01")// 通过该注解,向Spring容器声明,该类的加载需要依赖于Test01,当加载Test02时,要先去加载Test01!
public class Test02 {
    @PostConstruct
    public void init(){
       System.out.println(Test01.HELLO);
    }
    public Test02(){
        System.out.println(Test01.WORLD);
    }
}


查看打印结果:

hello
world

注:也可以使用@Order注解给Test01、Test02类声明加载优先级,优先加载Test01,然后再加载Test02!

相关文章
|
7月前
|
Java Spring 容器
Spring注解开发定义bean及纯注解开发模式
Spring注解开发定义bean及纯注解开发模式
61 0
|
7月前
|
Java Spring 容器
@Resource注解是什么作用,和@bean区别是什么?
@Resource注解是什么作用,和@bean区别是什么?
222 0
|
7月前
|
Java Spring 容器
面试题:在spring框架下面,Bean的属性lazy-init有什么作用,默认值是多少
面试题:在spring框架下面,Bean的属性lazy-init有什么作用,默认值是多少
61 0
|
7月前
|
Java 数据库连接 API
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
800 0
|
7月前
|
XML Java 数据格式
spring-bean配置信息重用(继承)和bean创建顺序是什么以及bean 对象的单例和多例讲解
spring-bean配置信息重用(继承)和bean创建顺序是什么以及bean 对象的单例和多例讲解
79 0
|
Java
SpringBoot AOP @Pointcut切入点execution表达式排除类中的具体方法
SpringBoot AOP @Pointcut切入点execution表达式排除类中的具体方法
459 0
Zp
@PostConstruct注解作用
@PostConstruct注解作用
Zp
161 0
|
Java Spring
SpringBoot 定义全局捕获异常类 @RestControllerAdvice 注解
SpringBoot 定义全局捕获异常类 @RestControllerAdvice 注解
461 0
SpringBoot 定义全局捕获异常类 @RestControllerAdvice 注解
|
Java Spring 容器
SpringBoot (走读源码)静态方法中调用spring注入的对象,注入对象为null?
SpringBoot (走读源码)静态方法中调用spring注入的对象,注入对象为null?
456 0
SpringBoot (走读源码)静态方法中调用spring注入的对象,注入对象为null?