<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          @PostConstruct注解是Spring提供的?今天講點不一樣的

          共 5744字,需瀏覽 12分鐘

           ·

          2021-06-03 05:04

          前言

          我們在講《Spring的Lifecycle》時提到,在Spring的使用中可以通過Lifecycle接口實現(xiàn)一些基于Spring容器生命周期邏輯。與此對照的就是通過@PostConstruct和@PreDestroy在Bean初始化或銷毀時執(zhí)行一些操作。

          很明顯Spring的Lifecycle是基于容器的生命周期來處理邏輯,而@PostConstruct和@PreDestroy是基于Bean的生命周期來處理業(yè)務邏輯。

          這里很多朋友就產生了一個誤解,以為@PostConstruct注解也是Spring提供的。其實不然,它是Java自帶的注解,下面我們就從頭來聊聊@PostConstruct注解。

          JSR-250規(guī)范

          在了解@PostConstruct注解之前,我們先來科普一個概念:JSR-250規(guī)范。

          JSR-250主要圍繞著“資源”的使用預定義了一些注解(Annotation),這里的“資源”可以理解為一個Class類的實例、一個JavaBean、或者一個Spring中的Bean。

          JSR-250相關的注解全部在javax.annotation和javax.annotation.security包中,包括:資源定義和權限控制。像我們經(jīng)常用到的@Resource、@PostConstruct、@PreDestroy、@Generated等都屬于這個規(guī)范中定義的注解。

          該規(guī)范并沒有提供具體的實現(xiàn)方式,僅僅是提供了指導性的文檔和幾個注解,由具體的框架去實現(xiàn)。

          也就是說,@PostConstruct注解并不是Spring提供的注解,只不過Spring按照JSR-250規(guī)范實現(xiàn)了規(guī)范中對@PostConstruct的約定。而別的框架,或者你自己寫一個框架,同樣可以按照約定進行實現(xiàn)。

          @PostConstruct的約定

          @PostConstruct和@PreDestroy是在Java EE 5引入的,位于javax.annotation包下,也就是java拓展包定義的注解。其中,javax中的x就是extension的意思。Java最初的設計者認為,這些功能并不是Java核心API,因此就放到了擴展包中,誰用誰實現(xiàn),按照約定就行。

          下面直接看看該類上的注解說明:

          “PostConstruct注釋用于在依賴關系注入完成之后需要執(zhí)行的方法上,以執(zhí)行任何初始化。此方法必須在將類放入服務之前調用。支持依賴關系注入的所有類都必須支持此注釋。即使類沒有請求注入任何資源,用PostConstruct注釋的方法也必須被調用。只有一個方法可以用此注釋進行注釋?!?/span>

          “應用PostConstruct注釋的方法必須遵守以下所有標準:

          • 該方法不得有任何參數(shù),除非是在EJB攔截器(interceptor)的情況下,它將帶有一個InvocationContext對象;

          • 該方法的返回類型必須為void;

          • 該方法不得拋出已檢查異常;

          • 應用PostConstruct的方法可以是public、protected、package private或private;

          • 除了應用程序客戶端之外,該方法不能是static;

          • 該方法可以是final;

          • 如果該方法拋出未檢查異常,那么不得將類放入服務中,除非是能夠處理異常并可從中恢復的EJB。

          除了上述約定,如果用在Servlet容器當中,還有有一定的處理時機。

          @PostConstruct的執(zhí)行時機

          下面所講的@PostConstruct的執(zhí)行時機是基于Spring的實現(xiàn)來講的。被@PostConstruct修飾的方法會在服務器加載Servlet時運行,并且只會被執(zhí)行一次。PostConstruct在構造函數(shù)之后執(zhí)行,init()方法之前執(zhí)行。

          對應的流程圖如下:

           

          實例演示

          理解了上面的基本概念,就先來看一個實例演示吧,使用起來非常簡單。

          基于Java 8的Spring Boot項目中添加如下類:

          @Service
          public class OrderService {

          public OrderService(){
          System.out.println("OrderService構造方法被執(zhí)行...");
          }

          @PostConstruct
          private void init() {
          System.out.println("PostConstruct注解方法被調用");
          }

          @PreDestroy
          private void shutdown() {
          System.out.println("PreDestroy注解方法被調用");
          }

          }

          啟動Spring Boot項目,控制臺打印日志如下:

          OrderService構造方法被執(zhí)行...
          PostConstruct注解方法被調用

          當關閉服務時,會打?。?/span>

          PreDestroy注解方法被調用

          通過實例,基本印證了上述說的理論。

          Java9的以后的移除

          在Java 8中我們可以直接使用對應的注解即可,但到Java 9及以后,J2EE棄用了@PostConstruct和@PreDestroy這兩個注解,并計劃在Java 11中將其刪除。

          針對這種情況,我們有兩種解決方案:第一添加額外的依賴;第二,換用其他的方式。

          第一種方案針對的是,你非要使用這個注解,或者說你的項目暫時沒辦法棄用這兩個注解。那么,可以手動添加依賴:

          <dependency>
          <groupId>javax.annotation</groupId>
          <artifactId>javax.annotation-api</artifactId>
          <version>1.3.2</version>
          </dependency>

          也就是說,雖然移除了,但是你把它們的依賴添加上,依舊還是可以用的。但此時也給我們提了一個醒兒,在項目中盡量別用這兩個注解了,Java 11都計劃將其移除了。

          此時,如果你使用的是Spring的項目,則可考慮另外一種方式,基于Spring的InitializingBean和DisposableBean接口來實現(xiàn)同樣的功能:

          @Service
          public class PaymentService implements InitializingBean, DisposableBean {

          public PaymentService(){
          System.out.println("PaymentService構造方法被執(zhí)行...");
          }

          @Override
          public void destroy() throws Exception {
          System.out.println("destroy方法被調用");
          }

          @Override
          public void afterPropertiesSet() throws Exception {
          System.out.println("afterPropertiesSet方法被調用");
          }
          }

          啟動項目,打印日志如下:

          PaymentService構造方法被執(zhí)行...
          afterPropertiesSet方法被調用

          停止項目,打印如下信息:

          destroy方法被調用

          也就是說在Spring的生態(tài)中,我們已經(jīng)有替代方案可實現(xiàn)了,而且是比較推薦的方式。

          其實Spring并沒有遵守約定

          在上面的約定中我們講到一個類中“只有一個方法可以用此注釋進行注釋”,在OrderService中再添加一個@PostConstruct注解的方法試試:

          @Service
          public class OrderService {

          public OrderService(){
          System.out.println("OrderService構造方法被執(zhí)行...");
          }

          @PostConstruct
          private void init() {
          System.out.println("PostConstruct注解方法被調用");
          }

          @PostConstruct
          private void init1() {
          System.out.println("PostConstruct init1 注解方法被調用");
          }

          @PreDestroy
          private void shutdown() {
          System.out.println("PreDestroy注解方法被調用");
          }

          }

          啟動程序,打印日志:

          OrderService構造方法被執(zhí)行...
          PostConstruct init1 注解方法被調用
          PostConstruct注解方法被調用

          不但沒報錯,而且兩個方法還都執(zhí)行了。這說明什么?這說明約定有時候就是用來被打破的,記住這一特殊情況就好。

          Spring中的實現(xiàn)原理

          以上是對@PostConstruct的簡單介紹,下面會從Spring源碼層面簡單分析一下實現(xiàn)原理。

          我們先來看一個Spring的接口BeanPostProcessor:

          public interface BeanPostProcessor {

          // 任何Bean實例化,并且Bean已經(jīng)populated(填充屬性) 就會回調這個方法
          Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

          // 任何Bean實例化,并且Bean已經(jīng)populated(填充屬性) 就會回調這個方法
          Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

          BeanPostProcessor是Spring IOC容器給我們提供的一個擴展接口,它兩個回調方法。當一個BeanPostProcessor的實現(xiàn)類注冊到Spring IOC容器后,對于該Spring IOC容器所創(chuàng)建的每個bean實例在初始化方法(如afterPropertiesSet和任意已聲明的init方法)調用前,將會調用BeanPostProcessor中的postProcessBeforeInitialization方法,而在bean實例初始化方法調用完成后,則會調用BeanPostProcessor中的postProcessAfterInitialization方法,整個調用順序可以簡單示意如下:

          --> Spring IOC容器實例化Bean
          --> 調用BeanPostProcessorpostProcessBeforeInitialization方法
          --> 調用bean實例的初始化方法
          --> 調用BeanPostProcessorpostProcessAfterInitialization方法

          而BeanPostProcessor有個實現(xiàn)類CommonAnnotationBeanPostProcessor,就是專門處理@PostConstruct和@PreDestroy注解。其中CommonAnnotationBeanPostProcessor的父類InitDestroyAnnotationBeanPostProcessor中,對應的調用邏輯如下:

          InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization()
          InitDestroyAnnotationBeanPostProcessor.findLifecycleMetadata()
          // 組裝生命周期元數(shù)據(jù)
          InitDestroyAnnotationBeanPostProcessor.buildLifecycleMetadata()
          // 查找@PostConstruct注釋的方法
          InitDestroyAnnotationBeanPostProcessor.initAnnotationType
          // 查找@PreDestroy注釋方法
          InitDestroyAnnotationBeanPostProcessor.destroyAnnotationType
          // 反射調用
          metadata.invokeInitMethods(bean, beanName);

          關于業(yè)務邏輯的處理細節(jié),這里就不再逐一展示,大家感興趣的話可以跟蹤一下源代碼。

          小結

          本篇文章我們需要留意幾點:第一,Spring只是實現(xiàn)了Java中對@PostConstruct注解定義的規(guī)范;第二,該注解在Java 9逐步開始廢棄,不建議再使用;第三,可采用Spring的InitializingBean和DisposableBean來替代對應的功能。



          往期推薦



          再聊面試,這次關于錢,關于培訓,關于內卷

          Spring的Lifecycle和SmartLifecycle,可以沒用過,但不能不知道!

          一個實例,輕松演示Spring Cloud集成Nacos實例

          篩選了幾百份簡歷,終于明白了為什么大廠要設學歷門檻了

          學習Nacos?咱先把服務搞起來,實戰(zhàn)教程~



          如果你覺得這篇文章不錯,那么,下篇通常會更好。添加微信好友,可備注“加群”(微信號:zhuan2quan)

          一篇文章就看透技術本質的人,
            和花一輩子都看不清的人,
            注定是截然不同的搬磚生涯。
          ▲ 按關注”程序新視界“,洞察技術內幕


          瀏覽 158
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产影视AV | 69激情视频 | 操妞视频在线观看免费网站 | 在线观看AA片 | 青娱乐cao |