Java中的@PostConstruct注解的使用
問(wèn)題
之前在做后端項(xiàng)目遇到一個(gè)奇怪的問(wèn)題,我裝配到Spring容器中的一個(gè)Bean在另外一個(gè)類(lèi)中無(wú)法被注入,該Bean的類(lèi)型如下:
@Component
@Data
public class FeishuCrawlerConfig{...}我使用@Component注解將其裝配到Spring容器中,然后在另外一個(gè)類(lèi)中將其自動(dòng)注入,格式如下:
@Component
public class FeishuTenantTokenEntity {
@Autowired
private FeishuCrawlerConfig feishuConfig;
...
}正常來(lái)講feishuConfig這個(gè)變量會(huì)被自動(dòng)注入完成初始化,但是后面我在使用這個(gè)變量時(shí)卻拋出了NPE。后來(lái)經(jīng)過(guò)跟同事的討論才發(fā)現(xiàn)我是在FeishuTenantTokenEntity的構(gòu)造方法中調(diào)用到了feishuConfig的方法,導(dǎo)致了NPE的產(chǎn)生。構(gòu)造方法如下:
public FeishuTenantTokenEntity() IOException {
// refreshTokenValue()方法中使用到feishuConfig的方法
this.tokenValue = refreshTokenValue();
this.accessTime = System.currentTimeMillis();
}產(chǎn)生這個(gè)問(wèn)題的原因是Spring中的Bean在初始化時(shí),會(huì)先執(zhí)行其構(gòu)造方法,再注入@Autowired注解標(biāo)注的其他Bean。我們的代碼中在構(gòu)造方法中就使用到了@Autowired注入的feishuConfig對(duì)象,這時(shí)它還沒(méi)有初始化完成,自然會(huì)拋出NPE。這個(gè)問(wèn)題的解決方法就是使用@PostConstruct注解標(biāo)注的方法替代FeishuTenantTokenEntity的構(gòu)造方法,該方法如下:
@PostConstruct
public void init() throws IOException {
this.tokenValue = refreshTokenValue();
this.accessTime = System.currentTimeMillis();
}Spring在初始化Bean時(shí),會(huì)在注入@Autowired注解標(biāo)注的Bean后執(zhí)行@PostConstruct注解標(biāo)注的方法。我們?cè)?code>init()方法中使用feishuConfig的方法顯然是沒(méi)問(wèn)題的,并且它還能替代構(gòu)造方法的作用。Spring中Bean初始化的執(zhí)行順序是構(gòu)造方法>依賴(lài)注入( @Autowired )> @PostConstruct標(biāo)注的方法。
@PostConstruct注解
講到這里,不得不細(xì)說(shuō)一下@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官方對(duì)@PostConstruct注解的注釋文檔,可以看到@PostConstruct注解是用于初始化方法上,該方法在依賴(lài)注入完成后執(zhí)行。Java對(duì)被@PostConstruct標(biāo)注的方法做出了如下限制:
- 除攔截器方法外,該方法不能有任何參數(shù)
- 被
@PostConstruct標(biāo)注的攔截器方法不能拋出application異常 - 該方法可以被public,protected,package private和private修飾
- 該方法不能為靜態(tài)的,但是可以被final修飾 簡(jiǎn)而言之,如果你的類(lèi)中的構(gòu)造方法需要使用到依賴(lài)注入的變量,你可以用
@PostConstruct標(biāo)注的方法來(lái)替代構(gòu)造方法完成初始化。
Spring如何實(shí)現(xiàn) @PostConstruct注解
我們知道在Spring中,Bean的初始化一般分為實(shí)例化,屬性賦值,初始化和銷(xiāo)毀這四個(gè)過(guò)程。在實(shí)例化階段的前后,InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation和postProcessAfterInstantiation方法會(huì)被調(diào)用。在初始化階段的前后,BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法會(huì)被調(diào)用。開(kāi)發(fā)者也可以繼承這些接口拓展功能。如下圖所示:

在這四個(gè)過(guò)程中間,Bean的依賴(lài)注入發(fā)生在屬性賦值這個(gè)階段。Spring會(huì)在postProcessBeforeInstantiation方法中也就是依賴(lài)注入完成之后調(diào)用@PostConstruct注解標(biāo)注的方法,完成Bean的部分初始化工作。 Spring的具體做法就是在創(chuàng)建Bean時(shí),會(huì)將它里面被@PostConstruct注解標(biāo)注的方法保存到Bean的元數(shù)據(jù)中,在后面調(diào)用postProcessBeforeInstantiation方法時(shí),會(huì)利用反射調(diào)用Bean的元數(shù)據(jù)中被 @PostConstruct注解標(biāo)注的方法,從而完成部分初始化工作。感興趣的同學(xué)可以看看源碼。
注意
Java官方已在Java 9中棄用了@PostConstruct注解,并在Java 11中刪除了@PostConstruct注解。 實(shí)現(xiàn)InitializingBean接口并重寫(xiě)其中的afterPropertiesSet方法也可以實(shí)現(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;
}到此這篇關(guān)于Java中的@PostConstruct注解的使用的文章就介紹到這了,更多相關(guān)Java @PostConstruct注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java注解之Retention、Documented、Inherited介紹
- 詳解Java的Spring框架中的注解的用法
- Java使用@Validated注解進(jìn)行參數(shù)驗(yàn)證的方法
- Java中l(wèi)ombok的@Builder注解的解析與簡(jiǎn)單使用詳解
- 全面解析Java中的注解與注釋
- 5分鐘搞懂java注解@Annotation的具體使用
- java中@ModelAttribute注解的作用
- Java注解Annotation與自定義注解詳解
- 由@NotNull注解引出的關(guān)于Java空指針的控制
- java 注解annotation的使用以及反射如何獲取注解
- 詳解Java編程中Annotation注解對(duì)象的使用方法
- Java Spring注解之@Async的基本用法和示例
相關(guān)文章
方法參數(shù)屬性params,@PathVariable和@RequestParam用法及區(qū)別
這篇文章主要介紹了方法參數(shù)屬性params,@PathVariable和@RequestParam用法及區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
System.identityHashCode和hashCode的區(qū)別及說(shuō)明
String調(diào)用hashCode()和System.identityHashCode()返回值不同是因?yàn)镾tring重寫(xiě)了hashCode()方法,而System.identityHashCode()返回對(duì)象的內(nèi)存地址哈希值;Test調(diào)用兩個(gè)方法返回值相同是因?yàn)門(mén)est沒(méi)有重寫(xiě)hashCode()方法,因此兩者調(diào)用底層的JVM_IHashCode方法返回相同值2024-11-11
Mybatis開(kāi)發(fā)要點(diǎn)-resultType和resultMap有什么區(qū)別詳解
本文主要介紹了Mybatis開(kāi)發(fā)要點(diǎn)-resultType和resultMap有什么區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
Java實(shí)現(xiàn)Excel數(shù)據(jù)驗(yàn)證功能
在Java中,開(kāi)發(fā)者可以使用一些開(kāi)源的庫(kù)(如Apache POI)來(lái)添加、修改和處理Excel中的數(shù)據(jù),下面我們就來(lái)看看如何使用Java實(shí)現(xiàn)添加,修改和刪除Excel數(shù)據(jù)驗(yàn)證吧2023-10-10
AOP在SpringBoot項(xiàng)目中的使用場(chǎng)景解讀
本文介紹如何使用AOP在不同場(chǎng)景下對(duì)方法執(zhí)行前進(jìn)行邏輯校驗(yàn),包括對(duì)整個(gè)包下、特定控制器下以及特定注解修飾的方法進(jìn)行校驗(yàn),通過(guò)自定義注解和切面實(shí)現(xiàn),展示了AOP的靈活性和強(qiáng)大功能2026-01-01
Java實(shí)現(xiàn)漢字轉(zhuǎn)全拼音的方法總結(jié)
在軟件開(kāi)發(fā)中,經(jīng)常會(huì)遇到需要將漢字轉(zhuǎn)換成拼音的場(chǎng)景,比如在搜索引擎優(yōu)化、數(shù)據(jù)存儲(chǔ)、國(guó)際化等方面,Java作為一種廣泛使用的編程語(yǔ)言,提供了多種方法來(lái)實(shí)現(xiàn)漢字到拼音的轉(zhuǎn)換,本文將詳細(xì)介紹幾種常用的Java漢字轉(zhuǎn)全拼音的方法,并提供具體的代碼示例和步驟2024-12-12
Spring Boot 開(kāi)發(fā)私有即時(shí)通信系統(tǒng)(WebSocket)
本文利用Spring Boot作為基礎(chǔ)框架,Spring Security作為安全框架,WebSocket作為通信框架,實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)聊天和群聊天2017-04-04
Java對(duì)MySQL數(shù)據(jù)庫(kù)進(jìn)行連接、查詢(xún)和修改操作方法
這篇文章主要介紹了Java對(duì)MySQL數(shù)據(jù)庫(kù)進(jìn)行連接、查詢(xún)和修改操作方法,需要的朋友可以參考下2017-07-07

