Java中的@PostConstruct注解的使用
問題
之前在做后端項目遇到一個奇怪的問題,我裝配到Spring容器中的一個Bean在另外一個類中無法被注入,該Bean的類型如下:
@Component @Data public class FeishuCrawlerConfig{...}
我使用@Component
注解將其裝配到Spring容器中,然后在另外一個類中將其自動注入,格式如下:
@Component public class FeishuTenantTokenEntity { @Autowired private FeishuCrawlerConfig feishuConfig; ... }
正常來講feishuConfig
這個變量會被自動注入完成初始化,但是后面我在使用這個變量時卻拋出了NPE。后來經過跟同事的討論才發(fā)現(xiàn)我是在FeishuTenantTokenEntity的構造方法中調用到了feishuConfig
的方法,導致了NPE的產生。構造方法如下:
public FeishuTenantTokenEntity() IOException { // refreshTokenValue()方法中使用到feishuConfig的方法 this.tokenValue = refreshTokenValue(); this.accessTime = System.currentTimeMillis(); }
產生這個問題的原因是Spring中的Bean在初始化時,會先執(zhí)行其構造方法,再注入@Autowired
注解標注的其他Bean。我們的代碼中在構造方法中就使用到了@Autowired
注入的feishuConfig
對象,這時它還沒有初始化完成,自然會拋出NPE。這個問題的解決方法就是使用@PostConstruct
注解標注的方法替代FeishuTenantTokenEntity的構造方法,該方法如下:
@PostConstruct public void init() throws IOException { this.tokenValue = refreshTokenValue(); this.accessTime = System.currentTimeMillis(); }
Spring在初始化Bean時,會在注入@Autowired
注解標注的Bean后執(zhí)行@PostConstruct
注解標注的方法。我們在init()
方法中使用feishuConfig
的方法顯然是沒問題的,并且它還能替代構造方法的作用。Spring中Bean初始化的執(zhí)行順序是構造方法>依賴注入( @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 <METHOD>(InvocationContext) * <p> * Object <METHOD>(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 <METHOD>() * </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
注解是用于初始化方法上,該方法在依賴注入完成后執(zhí)行。Java對被@PostConstruct
標注的方法做出了如下限制:
- 除攔截器方法外,該方法不能有任何參數(shù)
- 被
@PostConstruct
標注的攔截器方法不能拋出application異常 - 該方法可以被public,protected,package private和private修飾
- 該方法不能為靜態(tài)的,但是可以被final修飾 簡而言之,如果你的類中的構造方法需要使用到依賴注入的變量,你可以用
@PostConstruct
標注的方法來替代構造方法完成初始化。
Spring如何實現(xiàn) @PostConstruct注解
我們知道在Spring中,Bean的初始化一般分為實例化,屬性賦值,初始化和銷毀這四個過程。在實例化階段的前后,InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation和postProcessAfterInstantiation方法會被調用。在初始化階段的前后,BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法會被調用。開發(fā)者也可以繼承這些接口拓展功能。如下圖所示:
在這四個過程中間,Bean的依賴注入發(fā)生在屬性賦值這個階段。Spring會在postProcessBeforeInstantiation方法中也就是依賴注入完成之后調用@PostConstruct
注解標注的方法,完成Bean的部分初始化工作。 Spring的具體做法就是在創(chuàng)建Bean時,會將它里面被@PostConstruct
注解標注的方法保存到Bean的元數(shù)據(jù)中,在后面調用postProcessBeforeInstantiation方法時,會利用反射調用Bean的元數(shù)據(jù)中被 @PostConstruct
注解標注的方法,從而完成部分初始化工作。感興趣的同學可以看看源碼。
注意
Java官方已在Java 9中棄用了@PostConstruct
注解,并在Java 11中刪除了@PostConstruct
注解。 實現(xiàn)InitializingBean
接口并重寫其中的afterPropertiesSet
方法也可以實現(xiàn)@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注解內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Java注解之Retention、Documented、Inherited介紹
- 詳解Java的Spring框架中的注解的用法
- Java使用@Validated注解進行參數(shù)驗證的方法
- Java中l(wèi)ombok的@Builder注解的解析與簡單使用詳解
- 全面解析Java中的注解與注釋
- 5分鐘搞懂java注解@Annotation的具體使用
- java中@ModelAttribute注解的作用
- Java注解Annotation與自定義注解詳解
- 由@NotNull注解引出的關于Java空指針的控制
- java 注解annotation的使用以及反射如何獲取注解
- 詳解Java編程中Annotation注解對象的使用方法
- Java Spring注解之@Async的基本用法和示例
相關文章
Java使用WeakHashMap實現(xiàn)緩存自動清理
在 Java 中,內存管理是一個重要的話題,尤其是在涉及到緩存的實現(xiàn)時,如果緩存項不再被使用,我們希望它們能被自動清理,而不必手動刪除,WeakHashMap 就是 Java 提供的一種用于緩存和內存管理的工具,本文將深入探討如何利用 WeakHashMap 來實現(xiàn)緩存自動清理2025-01-01SpringBoot整合Jackson超詳細用法(附Jackson工具類)
這篇文章主要介紹了SpringBoot整合Jackson超詳細教程,本篇講的是Jackson的詳細用法,Jackson工具類在文章最后,直接復制粘貼即可使用,需要的朋友可以參考下2023-03-03SpringBoot使用SchedulingConfigurer實現(xiàn)多個定時任務多機器部署問題(推薦)
這篇文章主要介紹了SpringBoot使用SchedulingConfigurer實現(xiàn)多個定時任務多機器部署問題,定時任務多機器部署解決方案,方式一拆分,單獨拆分出來,單獨跑一個應用,方式二是基于aop攔截處理(搶占執(zhí)行),只要有一個執(zhí)行,其它都不執(zhí)行,需要的朋友可以參考下2023-01-01Java使用application.property讀取文件里面的值
本文通過實例代碼給大家介紹了Java使用application.property讀取文件里面的值,需要的朋友可以參考下2018-10-10基于Spring Data Jest的Elasticsearch數(shù)據(jù)統(tǒng)計示例
本篇文章主要介紹了基于Spring Data Jest的Elasticsearch數(shù)據(jù)統(tǒng)計示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題解決方案
這篇文章主要介紹了SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題,對SpringCloud Alibaba Feign請求頭問題感興趣的朋友跟隨小編一起看看吧2024-02-02