The PostConstruct annotation is often used in Spring and SpringBoot development for Bean initialization.

In the project code, the configuration is read from the configuration center and then initialized to the specified Bean. Anywhere else you need to get the configuration dynamically, you can dependency inject the Bean directly. Example code is as follows:

ApplicationConfig

The class in which the dynamic configuration resides, mainly properties.

@Configuration
@Data
@Slf4j
public class ApplicationConfig {

  /** * client host */
  private String host;

  /** * client port */
  private String port;

  public ApplicationConfig(a) {
    log.info("ApplicationConfig constructor execute");
  }

  @PostConstruct
  public void init(a) {
    log.info("ApplicationConfig postConstructor execute"); }}Copy the code
ApplicationConfigLoadService

The PostConstruct method is used to obtain configuration information from the remote configuration center.

@Service
@Slf4j
public class ApplicationConfigLoadService {

  @Resource
  private ApplicationConfig applicationConfig;

  public ApplicationConfigLoadService(a) {
    log.info("ApplicationConfigLoadService constructor execute");
  }

  @PostConstruct
  public void load(a) {
    log.info("ApplicationConfigLoadService postConstruct execute");
    // The configuration can be read from the database, or from a remote configuration center
    String host = "127.0.0.1";
    String port = "8080"; applicationConfig.setHost(host); applicationConfig.setPort(port); }}Copy the code
ApplicationClientFactory

Use ApplicationConfig to do some actions after class initialization, based on configuration information.

@Component
@Slf4j
public class ApplicationClientFactory {

  @Resource
  private ApplicationConfig applicationConfig;

  public ApplicationClientFactory(a) {
    log.info("ApplicationClientFactory constructor execute");
  }

  @PostConstruct
  public void init(a) {
    log.info("ApplicationClientFactory postConstruct execute, host:{}, port:{}", applicationConfig.getHost(), applicationConfig.getPort()); }}Copy the code

Remark:

  1. The main class provides a constructor with no arguments and an initialization method using the @postconstructor annotation to see the order of execution.
  2. Code instructions
    1. ApplicationConfigLoadService initialization method of loading configuration
    2. Setter methods of ApplicationConfig do the configuration initialization
    3. ApplicationClientFactory relies on properties in ApplicationConfig to do some initialization.

Run the above code and look at the log.

2021-06-06 15:38:28. 2790-591 the INFO [main] C.Y.M.C.A pplicationClientFactory: ApplicationClientFactory constructor to execute
2021-06-06 15:38:28. 2790-598 the INFO [main] C.Y.M.C onfiguration. ApplicationConfig: ApplicationConfig constructor to execute
2021-06-06 15:38:28. 2790-599 the INFO [main] C.Y.M.C onfiguration. ApplicationConfig: ApplicationConfig postConstructor execute
2021-06-06 15:38:28. 2790-599 the INFO [main] C.Y.M.C.A pplicationClientFactory: ApplicationClientFactory postConstruct execute, host:null, port:null
2021-06-06 15:38:28. 2790-602 the INFO [main] C.Y.M.C.A pplicationConfigLoadService: ApplicationConfigLoadService constructor execute
2021-06-06 15:38:28. 2790-603 the INFO [main] C.Y.M.C.A pplicationConfigLoadService: ApplicationConfigLoadService postConstruct execut
Copy the code

You can see that the ApplicationClientFactory constructor is executed first, then the ApplicationConfig constructor and the method that identifies the PostConstruct annotation are executed because they depend on the ApplicationConfig class. The ApplicationClientFactory’s own postConstruct method is then executed.

But it can be seen from the log, at this time due to ApplicationConfigLoadService haven’t be loaded, so read configuration are empty.

Try the solution

Option 1: Specify the loading order of beans using DependsOn.

Modify the code as follows:

Value is the name of the dependent Bean.

@DependsOn(value = {"applicationConfigLoadService"})
@Component
@Slf4j
public class ApplicationClientFactory  
Copy the code
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.
Copy the code

The JDK documentation shows that a DependsOn annotation is mainly used when the current Bean is not shown to be dependent on another Bean through properties or construction parameters, but does have some initialization action that is dependent on another Bean.

In the code example above, the problem is resolved by adding a DependsOn annotation.

2021-06-06 16:36:59. 3688-944 the INFO [main] C.Y.M.C.A pplicationConfigLoadService: ApplicationConfigLoadService constructor execute
2021-06-06 16:36:59. 3688-948 the INFO [main] C.Y.M.C onfiguration. ApplicationConfig: ApplicationConfig constructor to execute
2021-06-06 16:36:59. 3688-949 the INFO [main] C.Y.M.C onfiguration. ApplicationConfig: ApplicationConfig postConstructor execute
2021-06-06 16:36:59. 3688-949 the INFO [main] C.Y.M.C.A pplicationConfigLoadService: ApplicationConfigLoadService postConstruct execute
2021-06-06 16:36:59. 3688-950 the INFO [main] C.Y.M.C.A pplicationClientFactory: ApplicationClientFactory constructor to execute
2021-06-06 16:36:59. 3688-951 the INFO [main] C.Y.M.C.A pplicationClientFactory: ApplicationClientFactory postConstruct Execute, host:127.0.0.1, port:8080
Copy the code

Scenario 2: Shows the beans to be relied on injected via @Resource or @AutoWired

This can also be seen in DependsOn JDK code to resolve the issue by showing the dependency. As you can see from the signing log, when a dependency injection Bean is shown, the injected Bean executes the corresponding constructor and the initialization method of the @Postconstructor annotation in turn.

public class ApplicationClientFactory {

  @Resource
  private ApplicationConfig applicationConfig;

  // Display dependencies
  @Resource
  private ApplicationConfigLoadService applicationConfigLoadService;

  public ApplicationClientFactory(a) {
    log.info("ApplicationClientFactory constructor execute");
  }

  @PostConstruct
  public void init(a) {
    log.info("ApplicationClientFactory postConstruct execute, host:{}, port:{}", applicationConfig.getHost(), applicationConfig.getPort()); }}Copy the code

The execution result

2021-06-06 16:08:17. 3286-458 the INFO [main] C.Y.M.C.A pplicationClientFactory: ApplicationClientFactory constructor to execute
2021-06-06 16:08:17. 3286-464 the INFO [main] C.Y.M.C onfiguration. ApplicationConfig: ApplicationConfig constructor to execute
2021-06-06 16:08:17. 3286-465 the INFO [main] C.Y.M.C onfiguration. ApplicationConfig: ApplicationConfig postConstructor execute
2021-06-06 16:08:17. 3286-466 the INFO [main] C.Y.M.C.A pplicationConfigLoadService: ApplicationConfigLoadService constructor execute
2021-06-06 16:08:17. 3286-467 the INFO [main] C.Y.M.C.A pplicationConfigLoadService: ApplicationConfigLoadService postConstruct execute
2021-06-06 16:08:17. 3286-467 the INFO [main] C.Y.M.C.A pplicationClientFactory: ApplicationClientFactory postConstruct Execute, host:127.0.0.1, port:8080
Copy the code

You can see now that ApplicationConfig has an attribute value to rely on in the postConstruc of ApplicationClientFactory.

However, here there is a risk, because the applicationConfigLoadService this variable does not actually used in the current class, only to rely on its postConstruct method. For the follow-up maintenance of students, it is likely to be unintentionally removed.