美文网首页
对Spring PostConstruct注解的一点新认识

对Spring PostConstruct注解的一点新认识

作者: 艺超51iwowo | 来源:发表于2021-06-08 13:59 被阅读0次

    无论是Spring还是SpringBoot开发中,PostConstruct注解的使用频率还是比较高的,通常用于Bean初始化完成的一些动作。

    在项目代码中,会将配置从配置中心中读取,然后初始化到指定的Bean中。其他需要动态获取配置的地方,直接依赖注入这个Bean即可。
    示例代码如下:

    ApplicationConfig

    动态配置所在的类,主要是属性。

    @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

    从远程配置中心中获取配置信息,主要依赖PostConstruct方法。

    @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");
        // 可以是从数据库,或者远程的配置中心中读取配置
        String host = "127.0.0.1";
        String port = "8080";
        applicationConfig.setHost(host);
        applicationConfig.setPort(port);
      }
    }
    
    ApplicationClientFactory

    使用ApplicationConfig,基于配置信息,在类初始化完成后,做一些动作。

    @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());
      }
    }
    

    备注:

    1. 主要的类中,提供了一个无参的构造方法,以及一个使用了@PostConstructor注解的初始化方法,主要用于看一下执行顺序。
    2. 代码说明
      1. ApplicationConfigLoadService 的初始化方法中加载配置
      2. ApplicationConfig的setter方法完成配置初始化
      3. ApplicationClientFactory依赖ApplicationConfig中的属性完成一些初始化工作。

    将上述代码执行一下,并查看日志。

    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
    

    可以看到ApplicationClientFactory的构造方法先被执行,然后由于依赖ApplicationConfig类,所以ApplicationConfig的构造方法和标识了PostConstruct注解的方法被执行,然后才会执行ApplicationClientFactory自己的postConstruct方法。

    但是从日志中可以看出,此时由于ApplicationConfigLoadService还没被加载,所以读取到的配置都是空的。

    尝试的解决方案

    方案1:是可以采用DependsOn指定Bean的加载顺序。

    修改代码如下:

    value即为依赖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.
    

    从JDK文档可以看出,DependsOn注解主要的使用场景是当前Bean没有显示通过属性或者构造参数依赖另外一个Bean,但是却要依赖另外一个Bean的一些初始化动作。

    在上述代码示例中,通过添加DependsOn注解,可以解决问题。

    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
    

    方案2: 显示通过@Resource或者@Autowired注入待依赖的Bean

    在DependsOn的JDK代码中也可以看到,通过显示依赖可以解决问题。通过签名日志可以看出,当显示依赖注入某个Bean时,被注入Bean会依次执行对应的构造函数以及@PostConstructor注解的初始化方法。

    public class ApplicationClientFactory {
    
      @Resource
      private ApplicationConfig applicationConfig;
    
      // 显示依赖
      @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());
      }
    }
    

    执行结果

    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
    

    此时可以看到在ApplicationClientFactory的postConstruc中,依赖的ApplicationConfig是有对应属性值的。

    但是,此处会存在一个风险问题,由于applicationConfigLoadService这个变量在当前类中并未实际使用,仅仅是为了依赖其postConstruct方法。对于后续维护的同学,很有可能无意将其移除。

    相关文章

      网友评论

          本文标题:对Spring PostConstruct注解的一点新认识

          本文链接:https://www.haomeiwen.com/subject/enpheltx.html