A new understanding of Spring PostConstruct annotation

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:

  1. The main classes provide a parameterless construction method and an initialization method using @ PostConstructor annotation, which is mainly used to see the execution order.
  2. Code description
    1. Load the configuration in the initialization method of ApplicationConfigLoadService
    2. The setter method of ApplicationConfig completes configuration initialization
    3. 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.

Keywords: Java Spring Boot

Added by KoopaTroopa on Wed, 02 Feb 2022 14:52:46 +0200