1. 程式人生 > >品Spring:對@PostConstruct和@PreDestroy註解的處理方法

品Spring:對@PostConstruct和@PreDestroy註解的處理方法

在bean的例項化過程中,也會用到一系列的相關注解。

如@PostConstruct和@PreDestroy用來標記初始化和銷燬方法。

平常更多的是側重於應用,很少會有人去了解它背後發生的事情。

今天就來看下它們的原始碼,這樣它們對你來說就不再是黑盒子了,而且學習原始碼對每個技術人來說都是必經之路。

人們對事物的認知以及自己的做法,往往分為三個階段:

1)最初看一個事物,非常複雜,簡直沒有一點頭緒,此時很多人就會放棄。

2)過了一段時間後,發現整體來看沒有想象中的那麼難,此時很多人以為自己已經get到了,然後就停止不前。

3)隨著瞭解的深入,發現很多細節的處理,很多方方面面的考慮是自己無法想到的,此時才會有一點點的東西轉化為你的智慧,永遠的屬於你。只可惜能夠到達這一步的人已經鳳毛麟角了。


因此能夠到達第3步的人就已經很少了,如果你恰巧到達了,那麼需要做的就只剩下堅持了。

堅持了足夠的時間之後,你付出的努力對你付出的時間的累積效果(類似於高數中的積分)就會非常明顯,此時你已經站在了一個相對較高的位置。可以體會下“會當凌絕頂,一覽眾山小”。

學習原始碼就是要做到第2點和第3點。既要知道巨集觀的處理程序,也要知道一部分處理細節。

而且巨集觀過程很容易理解,很多時候猜都能猜出來,難就難在細節處理上。

因為巨集觀對應的是說,細節對應的是做,用嘴說說誰都會,一旦做起來就不是那麼回事了。

細節處理之所以難,是因為沒有找到一種很好的表述方法。

一個很重要但從不被重視的觀點:

要解決一個問題,首先要找到這個問題,並把它描述(表示)出來。

程式設計新說注:很多時候我們看到的只是現象,隱藏在現象背後的才是問題。

我想表達,其實如何把一個事物準確而又良好的描述出來,是很重要的。

在程式設計的世界裡,描述事物很大程度上對應於廣義的資料結構。

一、註解的描述

使用兩個Class<? extends Annotation>欄位儲存兩個註解,通過setter方法設定進來,而不是寫死。如下圖0102:


這樣做的好處是,如果你看不上Java提供的這兩個註解,完全可以自己定義兩個,把它們替換掉,效果完全一樣。

二、方法的描述

我們都能想到使用反射在一個類裡面找出所有標有註解的方法,就是Method型別的一個物件。

Spring也是這樣做的,但是它考慮的更加細節化。如下圖0304:



把Method儲存起來不足為奇,而且還要求方法的引數個數必須為0,即沒有引數。

還有一個String型別的identifier欄位,這個單詞是識別符號的意思。

看到下面對它的賦值,如果是私有方法,則是方法的全名(包括類名),否則是方法的簡單名(不包括類名)。

看到這裡,一定會覺得很奇怪,先賣個關子,到後面再說明為什麼。

還重寫了hashCode和equals方法,也是依賴identifier欄位來實現的。說明這個欄位非常重要。

還說明可能會把這個類的物件作為Map的key使用,或往Set集合中新增,其實都是在比較兩個物件是否相等。

三、類和方法的對應關係描述

其實我們需要知道的是每個類中的相應方法,所以要和類關聯起來。如下圖05:

 

我們可以看到有兩組欄位表示初始化和銷燬方法,一組是Collection型別,一組是Set型別。

Set型別是不能重複的,可以是無序的如HashSet,也可以是有序的如LinkedHashSet。

Collection型別是比較寬泛的介面,還可以有重複元素。

再看下構造方法,使用的是Collection型別,說明傳進來的初始化或銷燬方法存在重複或冗餘,需要進行一些處理,然後變為Set型別,把冗餘的過濾掉。

四、通過反射找出標有註解的方法

如下圖06:

 

 

整體思路並不難,這裡主要看幾個細節問題。

這裡提到了LocalMethods(即本地方法),指的是一個類自己宣告的方法,還有它實現的接口裡的預設方法。

就是從這些方法裡找出標有初始化和銷燬註解的方法。

然後再從這個類的父類裡面按照相同的方式找出父類中的這些方法。接著再找父類的父類。

說明這裡是支援繼承的,如C繼承B,B繼承A,那麼C、B、A中的註解方法都會被找出來。

這就是帶來一個問題,即順序該如何處理,是父類的在前還是子類的在前?

如果對面向物件比較熟悉的話,就會知道初始化屬於類的構造,銷燬屬於類的析構。

可以再進一步,把構造看作是構造方法,把析構看作是析構方法。所以其實就是構造方法和析構方法的呼叫順序了。

對於構造方法是父類的先執行,因為子類依賴父類,父類不構建好,子類無法構建。

對於析構方法是子類的先執行,同樣是因為子類依賴父類,如果父類先銷燬,子類的依賴就不存在了,它怎麼可能存活。

可以看出呼叫順序正好相反。可以把父類比作房子的一樓,子類比作房子的二樓,子類依賴父類,相當於二樓依賴一樓。

蓋樓時先一樓再二樓,對應於構造方法的呼叫順序。拆樓時先二樓再一樓,對應於析構方法的呼叫順序。

因此,標有初始化註解的方法是父類的方法在前面,子類的方法在後面。標有銷燬註解的方法正好倒過來,子類的在前,父類的在後。

構造方法和析構方法是不能被繼承和重寫的,但是標有註解的方法是可以被繼承(只要不是private)或重寫(只要不是private/final)的。

這就表明它們之間肯定會有些不同,上面提到的identifier欄位,和依賴它實現hashCode和equals方法,就是為了解決這個問題的。

看下它的值,非常特殊:

對於私有方法,identifier欄位的值就是方法全名,因為私有方法不能被繼續和重寫,子類裡和父類裡定義的同名私有方法,也是不同的兩個方法。

所以它們不能互相覆蓋,必須全部保留,因此用方法的全名,全名肯定是不同的,所以hashCode也不同。

對於非私有方法(一般是公共和受保護的),identifier欄位的值就是方法的簡單名,因為非私有方法可以被繼續和重寫。

子類裡和父類裡定義的同名非私有方法,雖然也是不同的兩個方法,但是它們以反射的方式在子類物件上呼叫時產生的結果是一樣的,都等同於呼叫子類上的方法。

所以此時只需保留一個就可以了,使用方法的簡單名,因為是一樣的,所以通過Set時就可以過濾掉一個,實際保留的是父類的,過濾掉的是子類的。

現在我們就明白了Spring設定identifier欄位的真正用意了。

程式設計新說注:父類中的Method物件,可以在子類的例項上invoke,得到的結果就是子類重寫方法後的結果。

五、對找出來的註解方法進行檢查

如下圖07:

 

看到把這些方法逐個新增到Set裡面,按照定義好的規則進行過濾。

最終的結果就是,私有同名方法都會被保留,非私有同名方法只會保留一個,由於順序的原因,初始化方法保留的是父類中的,銷燬方法保留的是子類中的。

六、通過反射來呼叫它們

當然,在框架開發中,使用反射來呼叫是很正常的事情。

如下圖0809:


到目前為止,我們已經瞭解了Spring對初始化和銷燬方法的處理邏輯。包括方法的表示,如何找出這些方法,方法的過濾去重與排序問題,以及方法的反射呼叫。

還有最後一個問題,就是這些處理要和bean後處理器的方法結合起來。

七、使用bean後處理器決定呼叫時機

共涉及到3個方法,

第一個,postProcessMergedBeanDefinition,如下圖10:

 

該方法雖然與合併後的bean定義相關,但卻不是用來處理bean定義的。

它一般用來做一些自我檢測的操作或準備和快取一些相關的元資料的操作。

在這裡就是把所有的初始化和銷燬方法都找出來,整理好並快取起來備用。其實這些就相當於元資料的處理。


第二個,postProcessBeforeInitialization,如下圖11:

 

在這裡完成對所有初始化方法的按順序呼叫。

第三個,postProcessBeforeDestruction,如下圖12:

 

在這裡完成對所有銷燬方法的按順序呼叫。

程式設計新說注:

本文介紹的都是@PostConstruct和@PreDestroy這兩個註解標註的方法。

除此之外還有其它方式也可以指定初始化和銷燬方法,上一篇文章中已寫明。

你還發現,這兩個註解可以標在多個方法上,還可以標在父類裡,且不受訪問控制符影響,因為私有方法也可以標。

而且當父類和子類中都有時,還知道了它們的呼叫順序。

以上這些都是細節問題,即使看官方文件,都不會寫的這麼詳細。這就是學習原始碼的好處。細節之處見真知。

最後用一句話共勉你我他:

很多事情,聽是一回事,看是一回事,說是一回事,做則是另一回事。

>>> 品Spring系列文章 <<<

 

品Spring:帝國的基石

品Spring:bean定義上梁山

品Spring:實現bean定義時採用的“先進生產力”

品Spring:註解終於“成功上位”

品Spring:能工巧匠們對註解的“加持”

品Spring:SpringBoot和Spring到底有沒有本質的不同?

品Spring:負責bean定義註冊的兩個“排頭兵”

品Spring:SpringBoot輕鬆取勝bean定義註冊的“第一階段”

品Spring:SpringBoot發起bean定義註冊的“二次攻堅戰”

品Spring:註解之王@Configuration和它的一眾“小弟們”

品Spring:bean工廠後處理器的呼叫規則

品Spring:詳細解說bean後處理器

 

>>> 熱門文章集錦 <<<

 

畢業10年,我有話說

相關推薦

Spring@PostConstruct@PreDestroy註解處理方法

在bean的例項化過程中,也會用到一系列的相關注解。如@PostConstruct和@PreDestroy用來標記初始化和銷燬方法。平常更多的是側重於應用,很少會有人去了解它背後發生的事情。今天就來看下它們的原始碼,這樣它們對你來說就不再是黑盒子了,而且學習原始碼對每個技術人來說都是必經之路。人們對事物的認知

Spring@Autowired@Value註解處理方法

在Spring中能夠完成依賴注入的註解有JavaSE提供的@Resource註解,就是上一篇文章介紹的。還有JavaEE提供的@javax.inject.Inject註解,這個用的很少,因為一般都不會去引用JavaEE的jar包。程式設計新說注:JavaEE早已經被Oracle拋棄了。JavaEE這個名字已經

Spring@Resource註解處理方法

@Resource是Java的註解,表示一個資源,它具有雙向的含義,一個是從外部獲取一個資源,一個是向外部提供一個資源。這其實就對應於Spring的注入和註冊。當它用在欄位和方法上時,表示前者。當它用在類上時表示後者。Spring只提供了對前者的支援。該註解本身表示的是資源,資源的含義是很寬泛的。由於絕大部分

@PostConstruct@PreDestroy註解

void stc des 異常 聲明 服務 多次 一個 徹底 使用netty框架的時候,會用到sharable對ChannelHandler進行註解, 被註解的ChannelHandler對應的同一個實例可以被加入到一個或者多個ChannelPipelines一次或者多次,

Spring註解之 @PostConstruct @PreDestroy

在工作中你不得不承認有千奇百怪的需求,比如今天讓你新增這,而明天讓你刪除那的。我對spring中bean生命週期的理解還是源於兩年前公司做的一個外包專案。那是真還是迷迷糊糊不懂。。。真是不懂spring中的大愛,大智慧。。,在此啪啪暗自打臉。 下面介紹一對cp @PostC

Spring學習總結(25)——註解@PostConstruct@PreDestroy使用總結

@PostConstruct 和 @PreDestroy這兩個都是方法的註解,將註解標註在方法上,該方法就能夠在應用啟動和銷燬時執行,@PostConstruct 註解標註的函式,是在類執行完建構函式之

Spring能工巧匠們註解的“加持”

問題的描述與方案的提出在Spring從XML轉向註解時,為了自身的開發方便,對註解含義進行了擴充(具體參考本號上一篇文章)。這個擴充直接導致了一個問題,就是需要從註解往元註解以及元元註解(即沿著從下向上的方向)裡傳遞資料。為了更好的描述這個問題,請再看個示例:@interface A {&

Spring註解之王@Configuration它的一眾“小弟們”

其實對Spring的瞭解達到一定程度後,你就會發現,無論是使用Spring框架開發的應用,還是Spring框架本身的開發都是圍繞著註解構建起來的。空口無憑,那就說個最普通的例子吧。在Spring中要啟用一項XXX功能,標準做法就是用@EnableXXX這種“啟用”型別的註解。那麼這種型

Spring MVC通過PageablePageableDefault註解獲取分頁信息(MongoDB通過Pageable來操作分頁)

組織 nbsp dal ref 註解 tro urn dir 個性 說明:Pageable同時也能用於操作MongoDB的分頁。 PageableSpring Data庫中定義的一個接口,該接口是所有分頁相關信息的一個抽象,通過該接口,我們可以得到和分頁相關所有信息(例如

spring啟動伺服器就載入@PostConstruct@PreDestroy

@PostConstruct 伺服器一啟動就呼叫 package com.cloudtech.web.listener; import javax.annotation.PostConstruct

spring配置xml配置java註解配置對比

雖然spring早就推出java註解的方式配置框架,由於習慣了看xml所以一直沒有去學習,然而最近需要學習springboot,為了平滑過渡,先被迫研究一下註解配置方式。 這裡展示3種spring配置檔案,來對比xml配置和java註解配置的區別,分別是spring\mv

通過Spring @PostConstruct @PreDestroy 方法 實現初始化銷燬bean之前進行的操作

關於在spring 容器初始化 bean 和銷燬前所做的操作定義方式有三種: 第一種:通過@PostConstruct 和 @PreDestroy 方法 實現初始化和銷燬bean之前進行的操作 第二種是:通過 在xml中定義init-method 和 d

SpringSpringBootSpring到底有沒有本質的不同?

現在的Spring相關開發都是基於SpringBoot的。最後在打包時可以把所有依賴的jar包都打進去,構成一個獨立的可執行的jar包。如下圖13: 使用java -jar命令就可以執行這個獨立的jar包。如下圖14: 這個jar包的執行入口就是一個main函式,典型的格式如下: @Spri

@PostConstruct @PreDestroy 指定初始化銷毀方法

fig 關閉 pri class text 調用 銷毀對象 int post 通過實現 @PostConstruct 和 @PreDestroy 註解,也可以指定 bean 的初始化和銷毀方法 一、Student 類 public class Student{

三十七、PostConstructPreDestroy

簡介 Java EE5 引入了@PostConstruct和@PreDestroy這兩個作用於Servlet生命週期的註解,實現Bean初始化之前和銷燬之前的自定義操作。此文主要說明@PostConstruct。 API使用說明 以下為@PostConstruct的API使用說明: Post

@PostConstruct@PreConstruct註解

code rec 卸載 des destroy 而且 靜態 異常 mem @PostConstruct和@PreConstruct。這兩個註解被用來修飾一個非靜態的void()方法.而且這個方法不能有拋出異常聲明。 @PostConstruct

Java開發之@PostConstruct@PreConstruct註解

     從Java EE5規範開始,Servlet增加了兩個影響Servlet生命週期的註解(Annotation):@PostConstruct和@PreDestroy。這兩個註解被用來修飾一個非靜態的void()方法.而且這個方法不能有丟擲異常宣告。 使用方式

pandas學習seriesdataframe進行排序

本問主要寫根據索引或者值對series和dataframe進行排序的方法 程式碼: #coding=utf-8 import pandas as pd import numpy as np #以下實現排序功能。 series=pd.Series([3,4,1,6],inde

Java開發之Tomcat註解@PostConstruct@PreConstruct註解

當我們啟動專案想要初始化一些初始化資料時可以用PostConstruct註解 本文轉載自https://www.cnblogs.com/landiljy/p/5764515.html 從Java EE5規範開始,Servlet增加了兩個影響Servlet生命週期的註解(Annotation)

spring security使用者名稱密碼的校驗過程

AuthenticationManager呼叫Provider,provider呼叫userDetaisService來根據username獲取真實的資料庫資訊。 而在usernamePasswordAuthenticationFilter中來呼叫的是Authentica