Java中的@PostConstruct注解的使用

 更新时间:2023年06月25日 08:38:25   作者:wild_man  
本文主要介绍了Java中的@PostConstruct注解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

问题

之前在做后端项目遇到一个奇怪的问题,我装配到Spring容器中的一个Bean在另外一个类中无法被注入,该Bean的类型如下:

@Component
@Data
public class FeishuCrawlerConfig{...}

我使用@Component注解将其装配到Spring容器中,然后在另外一个类中将其自动注入,格式如下:

@Component
public class FeishuTenantTokenEntity {
    @Autowired
    private FeishuCrawlerConfig feishuConfig;
    ...
}

正常来讲feishuConfig这个变量会被自动注入完成初始化,但是后面我在使用这个变量时却抛出了NPE。后来经过跟同事的讨论才发现我是在FeishuTenantTokenEntity的构造方法中调用到了feishuConfig的方法,导致了NPE的产生。构造方法如下:

public FeishuTenantTokenEntity() IOException {
    // refreshTokenValue()方法中使用到feishuConfig的方法
    this.tokenValue = refreshTokenValue();
    this.accessTime = System.currentTimeMillis();
}

产生这个问题的原因是Spring中的Bean在初始化时,会先执行其构造方法,再注入@Autowired注解标注的其他Bean。我们的代码中在构造方法中就使用到了@Autowired注入的feishuConfig对象,这时它还没有初始化完成,自然会抛出NPE。这个问题的解决方法就是使用@PostConstruct注解标注的方法替代FeishuTenantTokenEntity的构造方法,该方法如下:

@PostConstruct
public void init() throws IOException {
    this.tokenValue = refreshTokenValue();
    this.accessTime = System.currentTimeMillis();
}

Spring在初始化Bean时,会在注入@Autowired注解标注的Bean后执行@PostConstruct注解标注的方法。我们在init()方法中使用feishuConfig的方法显然是没问题的,并且它还能替代构造方法的作用。Spring中Bean初始化的执行顺序是构造方法>依赖注入( @Autowired )> @PostConstruct标注的方法

@PostConstruct注解

讲到这里,不得不细说一下@PostConstruct注解。

/**
 * The PostConstruct annotation is used on a method that needs to be executed
 * after dependency injection is done to perform any initialization. This
 * method MUST be invoked before the class is put into service. This
 * annotation MUST be supported on all classes that support dependenc
 * injection. The method annotated with PostConstruct MUST be invoked even
 * if the class does not request any resources to be injected. Only one
 * method can be annotated with this annotation. The method on which the
 * PostConstruct annotation is applied MUST fulfill all of the following
 * criteria:
 * <p>
 * <ul>
 * <li>The method MUST NOT have any parameters except in the case of
 * interceptors in which case it takes an InvocationContext object as
 * defined by the Interceptors specification.</li>
 * <li>The method defined on an interceptor class MUST HAVE one of the
 * following signatures:
 * <p>
 * void &#060;METHOD&#062;(InvocationContext)
 * <p>
 * Object &#060;METHOD&#062;(InvocationContext) throws Exception
 * <p>
 * <i>Note: A PostConstruct interceptor method must not throw application
 * exceptions, but it may be declared to throw checked exceptions including
 * the java.lang.Exception if the same interceptor method interposes on
 * business or timeout methods in addition to lifecycle events. If a
 * PostConstruct interceptor method returns a value, it is ignored by
 * the container.</i>
 * </li>
 * <li>The method defined on a non-interceptor class MUST HAVE the
 * following signature:
 * <p>
 * void &#060;METHOD&#062;()
 * </li>
 * <li>The method on which PostConstruct is applied MAY be public, protected,
 * package private or private.</li>
 * <li>The method MUST NOT be static except for the application client.</li>
 * <li>The method MAY be final.</li>
 * <li>If the method throws an unchecked exception the class MUST NOT be put into
 * service except in the case of EJBs where the EJB can handle exceptions and
 * even recover from them.</li></ul>
 * @since Common Annotations 1.0
 * @see javax.annotation.PreDestroy
 * @see javax.annotation.Resource
 */
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}

这是Java官方对@PostConstruct注解的注释文档,可以看到@PostConstruct注解是用于初始化方法上,该方法在依赖注入完成后执行。Java对被@PostConstruct标注的方法做出了如下限制:

  • 除拦截器方法外,该方法不能有任何参数
  • @PostConstruct标注的拦截器方法不能抛出application异常
  • 该方法可以被public,protected,package private和private修饰
  • 该方法不能为静态的,但是可以被final修饰 简而言之,如果你的类中的构造方法需要使用到依赖注入的变量,你可以用@PostConstruct标注的方法来替代构造方法完成初始化。

Spring如何实现 @PostConstruct注解

我们知道在Spring中,Bean的初始化一般分为实例化,属性赋值,初始化和销毁这四个过程。在实例化阶段的前后,InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation和postProcessAfterInstantiation方法会被调用。在初始化阶段的前后,BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法会被调用。开发者也可以继承这些接口拓展功能。如下图所示:

在这四个过程中间,Bean的依赖注入发生在属性赋值这个阶段。Spring会在postProcessBeforeInstantiation方法中也就是依赖注入完成之后调用@PostConstruct注解标注的方法,完成Bean的部分初始化工作。 Spring的具体做法就是在创建Bean时,会将它里面被@PostConstruct注解标注的方法保存到Bean的元数据中,在后面调用postProcessBeforeInstantiation方法时,会利用反射调用Bean的元数据中被 @PostConstruct注解标注的方法,从而完成部分初始化工作。感兴趣的同学可以看看源码。

注意

Java官方已在Java 9中弃用了@PostConstruct注解,并在Java 11中删除了@PostConstruct注解。 实现InitializingBean接口并重写其中的afterPropertiesSet方法也可以实现@PostConstruct注解相同的功能。

/**
 * Interface to be implemented by beans that need to react once all their properties
 * have been set by a {@link BeanFactory}: e.g. to perform custom initialization,
 * or merely to check that all mandatory properties have been set.
 *
 * <p>An alternative to implementing {@code InitializingBean} is specifying a custom
 * init method, for example in an XML bean definition. For a list of all bean
 * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see DisposableBean
 * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues()
 * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName()
 */
public interface InitializingBean {
   /**
    * Invoked by the containing {@code BeanFactory} after it has set all bean properties
    * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
    * <p>This method allows the bean instance to perform validation of its overall
    * configuration and final initialization when all bean properties have been set.
    * @throws Exception in the event of misconfiguration (such as failure to set an
    * essential property) or if initialization fails for any other reason
    */
   void afterPropertiesSet() throws Exception;
}

到此这篇关于Java中的@PostConstruct注解的使用的文章就介绍到这了,更多相关Java @PostConstruct注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 方法参数属性params,@PathVariable和@RequestParam用法及区别

    方法参数属性params,@PathVariable和@RequestParam用法及区别

    这篇文章主要介绍了方法参数属性params,@PathVariable和@RequestParam用法及区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • System.identityHashCode和hashCode的区别及说明

    System.identityHashCode和hashCode的区别及说明

    String调用hashCode()和System.identityHashCode()返回值不同是因为String重写了hashCode()方法,而System.identityHashCode()返回对象的内存地址哈希值;Test调用两个方法返回值相同是因为Test没有重写hashCode()方法,因此两者调用底层的JVM_IHashCode方法返回相同值
    2024-11-11
  • Mybatis开发要点-resultType和resultMap有什么区别详解

    Mybatis开发要点-resultType和resultMap有什么区别详解

    本文主要介绍了Mybatis开发要点-resultType和resultMap有什么区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java实现Excel数据验证功能

    Java实现Excel数据验证功能

    在Java中,开发者可以使用一些开源的库(如Apache POI)来添加、修改和处理Excel中的数据,下面我们就来看看如何使用Java实现添加,修改和删除Excel数据验证吧
    2023-10-10
  • AOP在SpringBoot项目中的使用场景解读

    AOP在SpringBoot项目中的使用场景解读

    本文介绍如何使用AOP在不同场景下对方法执行前进行逻辑校验,包括对整个包下、特定控制器下以及特定注解修饰的方法进行校验,通过自定义注解和切面实现,展示了AOP的灵活性和强大功能
    2026-01-01
  • Java实现汉字转全拼音的方法总结

    Java实现汉字转全拼音的方法总结

    在软件开发中,经常会遇到需要将汉字转换成拼音的场景,比如在搜索引擎优化、数据存储、国际化等方面,Java作为一种广泛使用的编程语言,提供了多种方法来实现汉字到拼音的转换,本文将详细介绍几种常用的Java汉字转全拼音的方法,并提供具体的代码示例和步骤
    2024-12-12
  • Spring Boot 开发私有即时通信系统(WebSocket)

    Spring Boot 开发私有即时通信系统(WebSocket)

    本文利用Spring Boot作为基础框架,Spring Security作为安全框架,WebSocket作为通信框架,实现点对点聊天和群聊天
    2017-04-04
  • Java 方法签名详解及实例代码

    Java 方法签名详解及实例代码

    这篇文章主要介绍了 Java 方法签名详解及实例代码的相关资料,需要的朋友可以参考下
    2016-10-10
  • Java对MySQL数据库进行连接、查询和修改操作方法

    Java对MySQL数据库进行连接、查询和修改操作方法

    这篇文章主要介绍了Java对MySQL数据库进行连接、查询和修改操作方法,需要的朋友可以参考下
    2017-07-07
  • MyBatis实现配置加载的步骤

    MyBatis实现配置加载的步骤

    本文主要介绍了MyBatis实现配置加载的步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05

最新评论