In both Spring and SpringBoot development, the PostConstruct annotation is frequently used. It is usually used for some actions completed by Bean initialization.
In the project code, the configuration will be read from the configuration center, and then initialized to the specified Bean. For other places that need to obtain the configuration dynamically, you can directly rely on and inject this Bean.
The example code is as follows:
ApplicationConfig
The class of dynamic configuration is mainly attribute.
@Configuration @Data @Slf4j public class ApplicationConfig { /** * client host */ private String host; /** * client port */ private String port; public ApplicationConfig() { log.info("ApplicationConfig constructor execute"); } @PostConstruct public void init() { log.info("ApplicationConfig postConstructor execute"); } }
ApplicationConfigLoadService
Obtaining configuration information from the remote configuration center mainly depends on the PostConstruct method.
@Service @Slf4j public class ApplicationConfigLoadService { @Resource private ApplicationConfig applicationConfig; public ApplicationConfigLoadService() { log.info("ApplicationConfigLoadService constructor execute"); } @PostConstruct public void load() { log.info("ApplicationConfigLoadService postConstruct execute"); // The configuration can be read from the database or remote configuration center String host = "127.0.0.1"; String port = "8080"; applicationConfig.setHost(host); applicationConfig.setPort(port); } }
ApplicationClientFactory
Use ApplicationConfig to do some actions after class initialization based on the configuration information.
@Component @Slf4j public class ApplicationClientFactory { @Resource private ApplicationConfig applicationConfig; public ApplicationClientFactory() { log.info("ApplicationClientFactory constructor execute"); } @PostConstruct public void init() { log.info("ApplicationClientFactory postConstruct execute, host:{}, port:{}", applicationConfig.getHost(), applicationConfig.getPort()); } }
remarks:
- The main classes provide a parameterless construction method and an initialization method using @ PostConstructor annotation, which is mainly used to see the execution order.
- Code description
- Load the configuration in the initialization method of ApplicationConfigLoadService
- The setter method of ApplicationConfig completes configuration initialization
- ApplicationClientFactory relies on the properties in ApplicationConfig to complete some initialization.
Execute the above code and view the log.
2021-06-06 15:38:28.591 INFO 2790 --- [ main] c.y.m.c.ApplicationClientFactory : ApplicationClientFactory constructor execute 2021-06-06 15:38:28.598 INFO 2790 --- [ main] c.y.m.configuration.ApplicationConfig : ApplicationConfig constructor execute 2021-06-06 15:38:28.599 INFO 2790 --- [ main] c.y.m.configuration.ApplicationConfig : ApplicationConfig postConstructor execute 2021-06-06 15:38:28.599 INFO 2790 --- [ main] c.y.m.c.ApplicationClientFactory : ApplicationClientFactory postConstruct execute, host:null, port:null 2021-06-06 15:38:28.602 INFO 2790 --- [ main] c.y.m.c.ApplicationConfigLoadService : ApplicationConfigLoadService constructor execute 2021-06-06 15:38:28.603 INFO 2790 --- [ main] c.y.m.c.ApplicationConfigLoadService : ApplicationConfigLoadService postConstruct execut
You can see that the construction method of ApplicationClientFactory is executed first, and then because it depends on the ApplicationConfig class, the construction method of ApplicationConfig and the method identifying the postconstruct annotation are executed, and then the postconstruct method of ApplicationClientFactory itself is executed.
However, it can be seen from the log that the read configuration is empty at this time because the ApplicationConfigLoadService has not been loaded.
Tried solutions
Scheme 1: Yes, you can use DependsOn to specify the loading order of beans.
The modification code is as follows:
value is the name of the dependent Bean.
@DependsOn(value = {"applicationConfigLoadService"}) @Component @Slf4j public class ApplicationClientFactory
Beans on which the current bean depends. Any beans specified are guaranteed to be created by the container before this bean. Used infrequently in cases where a bean does not explicitly depend on another through properties or constructor arguments, but rather depends on the side effects of another bean's initialization.
As can be seen from the JDK document, the main usage scenario of the DependsOn annotation is that the current Bean does not display that it depends on another Bean through attributes or construction parameters, but it depends on some initialization actions of another Bean.
In the above code example, the problem can be solved by adding the DependsOn annotation.
2021-06-06 16:36:59.944 INFO 3688 --- [ main] c.y.m.c.ApplicationConfigLoadService : ApplicationConfigLoadService constructor execute 2021-06-06 16:36:59.948 INFO 3688 --- [ main] c.y.m.configuration.ApplicationConfig : ApplicationConfig constructor execute 2021-06-06 16:36:59.949 INFO 3688 --- [ main] c.y.m.configuration.ApplicationConfig : ApplicationConfig postConstructor execute 2021-06-06 16:36:59.949 INFO 3688 --- [ main] c.y.m.c.ApplicationConfigLoadService : ApplicationConfigLoadService postConstruct execute 2021-06-06 16:36:59.950 INFO 3688 --- [ main] c.y.m.c.ApplicationClientFactory : ApplicationClientFactory constructor execute 2021-06-06 16:36:59.951 INFO 3688 --- [ main] c.y.m.c.ApplicationClientFactory : ApplicationClientFactory postConstruct execute, host:127.0.0.1, port:8080
Scenario 2: display the Bean to be dependent injected through @ Resource or @ Autowired
It can also be seen in the JDK code of DependsOn that the problem can be solved by displaying dependencies. It can be seen from the signature log that when dependency injection is displayed into a Bean, the injected Bean will execute the corresponding constructor and the initialization method of @ PostConstructor annotation in turn.
public class ApplicationClientFactory { @Resource private ApplicationConfig applicationConfig; // Display dependency @Resource private ApplicationConfigLoadService applicationConfigLoadService; public ApplicationClientFactory() { log.info("ApplicationClientFactory constructor execute"); } @PostConstruct public void init() { log.info("ApplicationClientFactory postConstruct execute, host:{}, port:{}", applicationConfig.getHost(), applicationConfig.getPort()); } }
results of enforcement
2021-06-06 16:08:17.458 INFO 3286 --- [ main] c.y.m.c.ApplicationClientFactory : ApplicationClientFactory constructor execute 2021-06-06 16:08:17.464 INFO 3286 --- [ main] c.y.m.configuration.ApplicationConfig : ApplicationConfig constructor execute 2021-06-06 16:08:17.465 INFO 3286 --- [ main] c.y.m.configuration.ApplicationConfig : ApplicationConfig postConstructor execute 2021-06-06 16:08:17.466 INFO 3286 --- [ main] c.y.m.c.ApplicationConfigLoadService : ApplicationConfigLoadService constructor execute 2021-06-06 16:08:17.467 INFO 3286 --- [ main] c.y.m.c.ApplicationConfigLoadService : ApplicationConfigLoadService postConstruct execute 2021-06-06 16:08:17.467 INFO 3286 --- [ main] c.y.m.c.ApplicationClientFactory : ApplicationClientFactory postConstruct execute, host:127.0.0.1, port:8080
At this point, you can see that in the postconstruct of ApplicationClientFactory, the dependent ApplicationConfig has corresponding attribute values.
However, there is a risk problem here, because the variable applicationConfigLoadService is not actually used in the current class, just to rely on its postConstruct method. For the students of subsequent maintenance, it is likely that they have no intention to remove it.