Stepping on the pit: @ PostConstruct, @ DependsOn, @ Order annotations are nested to solve the problem of Bean loading priority

Posted by adityakonda on Sun, 12 Dec 2021 02:51:40 +0100

Stepping on pits: @ PostConstruct, @ DependsOn, @ Order annotation nested use cases

Today, when writing the requirement code at work, I encountered a problem about the loading priority of the Spring Bean object. Combined with the Spring source code, I roughly summarized three commonly used annotations when I encountered the Bean loading Order requirement: @ PostConstruct, @ DependsOn, @ Order.

1, Role of @ Order annotation

  • @The Order annotation is used to define the priority of the execution Order of beans in the Spring IOC container.

Use case:

@Component
@Order(0)
public class Test01 {
   ...
}

@Component
@Order(1)
public class Test02 {
   ...
}

@Component
@Order(2)
public class Test03 {
   ...
}
Copy code

As shown in the above code, the priority is defined through the @ Order annotation. The loading Order of the three Bean objects from the IOC container is: Test01, Test02 and Test03.

2, Role of @ PostConstruct annotation

  • @The PostConstruct annotation can be used to decorate a non static method with a return value type of void (eg: myInit()).
  • This method (myInit()) will be executed when the server loads the Servlet, and will only be executed once!
  • The call of this method (myInit()) is executed after the constructor, before the init() method of the Servlet, and after the destroy() method of the Servlet.

Use case:

@Component
public class Test {
   @PostConstruct
   private void init() {
      // initialization
      System.out.println("World!");
   } 
  
   public Test(){
       System.out.println("Hello");
   }
}
Copy code

Output results:

Hello
World!
Copy code

3, Role of @ DependsOn annotation

  • The function of this annotation, as the name suggests, is "who depends on whom".
  • If @ DependsOn(value = "test01") is added to the Test02 class, it means that Test02 depends on Test01 when loading. The Spring IOC container will load Test01 first and then Test02.

As an example of an actual business scenario, suppose there are two classes Test01 and Test02 that need to be managed by the Spring IOC container:

/**
 * Test01 Is a class with 1 static variable
 */
@Component
public class Test01 {
    // The attribute value of the static variable needs to be assigned through the Spring container, and the value (hello) is defined in application Properties.
    // Note: @ Value annotation cannot inject attribute values into static variables (otherwise the injection result obtained is null)!
    // Therefore, the attribute Value injection of HELLO needs to add @ Value annotation to the setter method. Refer to the article:[ https://blog.csdn.net/weixin_43591980/article/details/121503720 ]
    public static String HELLO;
  
    public static String WORLD;
  
    @Value("${spring.test.hello}")// The value is hello
    public void setHELLO(String hello) {
        HELLO = hello;
    }
  
    @Value("${spring.test.world}")// The value is world
    public void setWORLD(String world) {
        WORLD = easak;
    }
}
Copy code

Let's look at the code of Test02 class (premise: Test02 class needs to be initialized and called first when our Spring Boot project starts!):

/**
 * Test02 It has an init() initialization method modified by @ PostConstruct annotation and a parameterless constructor
 */
@Component
public class Test02 {
    @PostConstruct
    public void init(){
       ...
    }
  
    public Test02(){
 				... 
    }
}
Copy code

Business requirements: I need to print the HELLO static variable value in Test01 class on the console when the parameter free construction method of Test02 is loaded, and then print the WORLD static variable value in Test01 class on the console when the init() method is executed.

At the beginning, my first thought was that it would be better to write it like this:

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

However, the final result printed on the console is:

null
null
 Copy code

Why? Why is this result?

  • Because the Test02 class will be initialized and called first when our Spring Boot project is started, that is, the IOC container will load the Test02 object first. At this time, Test01 has not been loaded into the container. At this time, the attribute values of the two static variables HELLO and WORLD in Test01 have not been injected through the @ Value annotation, so the result should be null ~

Solution: use @ DependsOn annotation

We improved the Test02 class:

@Component
@DependsOn(value = "test01")// Through this annotation, declare to the Spring container that the loading of this class depends on Test01. When loading Test02, load Test01 first!
public class Test02 {
    @PostConstruct
    public void init(){
       System.out.println(Test01.HELLO);
    }
  
    public Test02(){
 				System.out.println(Test01.WORLD);
    }
}
Copy code

View print results:

hello
world
 Copy code

Note: you can also use the @ Order annotation to declare the loading priority for Test01 and Test02 classes. Load Test01 first, and then load Test02!