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!