Stomp pit: @postconstruct, @Dependson, @Order annotation nested use case

When I was writing requirement code today, I met a problem about the loading priority of Sping Bean object. Combined with Spring source code, I roughly sum up three annotations commonly used when meeting the requirement of Bean loading Order: @postconstruct, @dependson and @Order.

The @order annotation

  • @OrderAnnotations define the priority of the order of execution of beans in the Spring IOC container.

Use cases:

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

As shown in the code above, with the @Order annotation defining priority, the three Bean objects are loaded from the IOC container in the Order Test01, Test02, and Test03.

The @postconstruct annotation

  • @PostConstructAnnotations can be used to modify a non-static return value of typevoid(eg:myInit()).
  • This method (myInit()) is executed when the server loads the Servlet, and only once!
  • This method (myInit()) is called after the constructor, before the init() method of the Servlet, and after the destroy() method of the Servlet.

Use cases:

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

Output result:

Hello
World!
Copy the code

3. The role of @dependson annotations

  • The function of this note is, as the name suggests, “who depends on whom.”
  • Suppose we add to class Test02@DependsOn(value = "test01")The Spring IOC container will load Test01 first and then Test02.

As an example of a real business scenario, suppose you now have two classes Test01 and Test02 that need to be hosted by the Spring IOC container:

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

Let’s look at the code for class Test02 (prerequisite: Class Test02 needs to be initialized first when our Spring Boot project starts!). :

/** * Test02 has one@PostConstructThe annotated init() initialization method and the no-argument constructor */
@Component
public class Test02 {
    @PostConstruct
    public void init(a){... }public Test02(a){... }}Copy the code

Business requirement: I need the console to print the value of the HELLO static variable in class Test01 when Test02’s no-argument constructor is loaded, and then theinit()When the Test01 method executes, the console prints the value of the WORLD static variable in class Test01.

So my first thought was, just write it like this:

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

But the final console print is:

null
null
Copy the code

Why? Why this result?

  • Because,The Test02 class will be the first to be initialized when our Spring Boot project startsIn other words, the IOC container will load Test02 first, but Test01 has not been loaded into the container, and the two static variables HELLO and WORLD in Test01 have not passed@ValueThe annotation injects the property value, so the result should be null ~

Solution: Use @dependson annotations

Class Test02 has been modified:

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

View the print result:

hello
world
Copy the code

Note: You can also use the @order annotation to specify a loading priority for class Test01 and class Test02.