@PostConstruct注解详解
簡介
javaEE5引入了@PostConstruct和@PreDestroy兩個作用于Servlet生命周期的注解,實現Bean初始化之前和銷毀之前的自定義操作
使用場景
在項目中主要是在Servlet初始化之前加載一些緩存數據等
API使用說明
PostConstruct 注釋用于在依賴關系注入完成之后需要執行的方法上,以執行任何初始化。此方法必須在將類放入服務之前調用。支持依賴關系注入的所有類都必須支持此注釋。即使類沒有請求注入任何資源,用 PostConstruct 注釋的方法也必須被調用。只有一個方法可以用此注釋進行注釋。應用 PostConstruct 注釋的方法必須遵守以下所有標準:該方法不得有任何參數,除非是在 EJB 攔截器 (interceptor) 的情況下,根據 EJB 規范的定義,在這種情況下它將帶有一個 InvocationContext 對象 ;該方法的返回類型必須為 void;該方法不得拋出已檢查異常;應用 PostConstruct 的方法可以是 public、protected、package private 或 private;除了應用程序客戶端之外,該方法不能是 static;該方法可以是 final;如果該方法拋出未檢查異常,那么不得將類放入服務中,除非是能夠處理異常并可從中恢復的 EJB。
特點:
1、只有一個非靜態方法能使用此注解
2、被注解的方法不得有任何參數
3、被注解的方法返回值必須為void
4、被注解方法不得拋出已檢查異常
5、此方法只會被執行一次
servlet執行流程
注意事項
使用此注解時會影響服務啟動時間。服務啟動時會掃描WEB-INF/classes的所有文件和WEB-INF/lib下的所有jar包。
@PostConstruct注解的用法
@PostConstruct是java5的時候引入的注解,指的是在項目啟動的時候執行這個方法,也可以理解為在spring容器啟動的時候執行,可作為一些數據的常規化加載,比如數據字典之類的。
被@PostConstruct修飾的方法會在服務器加載Servle的時候運行,并且只會被服務器執行一次。PostConstruct在構造函數之后執行
也就是加載順序
服務器加載Servlet -> servlet 構造函數的加載 -> postConstruct ->init(init是在service 中的初始化方法. 創建service 時發生的事件.) ->Service->destory->predestory->服務器卸載serlvet
那么問題:spring中Constructor、@Autowired、@PostConstruct的順序
Constructor >> @Autowired >> @PostConstruct
依賴注入的字面意思就可以知道,要將對象p注入到對象a,那么首先就必須得生成對象p與對象a,才能執行注入。所以,如果一個類A中有個成員變量p被@Autowired注解,那么@Autowired注入是發生在A的構造方法執行完之后的。
@PostConstruct應用場景:
如果想在生成對象時候完成某些初始化操作,而偏偏這些初始化操作又依賴于依賴注入,那么就無法在構造函數中實現。為此,可以使用@PostConstruct注解一個方法來完成初始化,@PostConstruct注解的方法將會在依賴注入完成后被自動調用。
在最近的工作中,get到一個很實用的注解,分享給諸位。
痛點
做過微信或支付寶支付的童鞋,可能遇到過這種問題,就是填寫支付結果回調,就是在支付成功之后,支付寶要根據我們給的地址給我們進行通知,通知我們用戶是否支付成功,如果成功我們就要去處理下面相應的業務邏輯,如果在測試服務,那么這個回調地址我們就需要填寫測試服務的,如果發布到線上那么我們就需要改成線上的地址。
針對上面的場景,我們一般都會通過如下的方式,進行一個動態配置,不需要每次去改,防止出現問題。
public class PayTest {@Value("${spring.profiles.active}")private String environment;public Object notify(HttpServletRequest request) {if ("prod".equals(environment)) {// 正式環境} else if ("test".equals(environment)) {// 測試環境}return "SUCCESS";} }上面的代碼看起來沒有一點問題,但是身為搬磚的我們咋可能這樣搬,姿勢不對呀!
問題:
擴展性太差,如果這個參數我們還需要在別的地方用到,那么我們是不是還要使用@Value的注解獲取一遍,假如有天我們的leader突然說嗎,test這個單詞看著太low了,換個高端一點的,換成dev,那么我們是不是要把項目中所有的test都要改過來,如果少還好,要是很多,那我們怕不是涼了。
所以我們能不能將這些配置參數搞成一個全局的靜態變量,這樣的話我們直接飲用就好了,哪怕到時候真的要改,那我也只需要改動一處就好了。
注意大坑
有的朋友可能就比較自信了,那我直接加個static修飾下不就好了,如果你真是打算這樣做,那你就準備卷好鋪蓋走人吧。直接加static獲取到的值其實是一個null,至于原因,大家復習下類以及靜態變量變量的加載順序。
@PostConstruct注解
那么既然說出了問題,肯定就有解決方法,不然你以為我跟你玩呢。
首先這個注解是由Java提供的,它用來修飾一個非靜態的void方法。它會在服務器加載Servlet的時候運行,并且只運行一次。
改造:
@Component public class SystemConstant {public static String surroundings;@Value("${spring.profiles.active}")public String environment;@PostConstructpublic void initialize() {System.out.println("初始化環境...");surroundings = this.environment;} }結果:
我們可以看到在項目啟動的時候進行了初始化
到這里我們已經可以拿到當前運行的環境是測試還是正式,這樣就可以做到動態配置
最后想說
其實這個注解遠不止這點用處,像我之前寫的Redis工具類,我使用的是RedisTemplate操作Redis,導致寫出來的方法沒辦法用static修飾,每次使用Redis工具類只能先注入到容器然后再調用,使用了這個注解就可以完美的解決這種尷尬的問題。代碼如下。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** @ClassName RedisUtil* @Description TODO* @Version 1.0*/ @Component public class RedisUtil {private static RedisTemplate<Object, Object> redisTemplates;@Autowiredprivate RedisTemplate<Object, Object> redisTemplate;@PostConstructpublic void initialize() {redisTemplates = this.redisTemplate;}/*** 添加元素** @param key* @param value*/public static void set(Object key, Object value) {if (key == null || value == null) {return;}redisTemplates.opsForValue().set(key, value);} }總結
以上是生活随笔為你收集整理的@PostConstruct注解详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jdk中提供的Collection、Co
- 下一篇: c++17(26)-数组、二维数组的指针