這是一篇對javaweb BaseServlet 自動封裝數據並調用service方法中提到的內容的補充說明,因爲篇幅太大單獨拿出來講明html
首先在循環判斷中,咱們作的操做就是一一識別方法的參數表各個位置上須要的都是什麼數據類型,Class對象指向方法的參數表上的各個位置
java
若是是請求頭就給請求頭,若是是響應頭就給響應頭,若是是實體類就給實體類對象,若是是其餘的狀況就躺平吧,這個servlet封裝方法還作不到面對那麼複雜的狀況,須要使用spring的四個包進行輔助,在這裏先不說了。web
分析現狀
因此如今咱們默認處理的方法參數列表中,只有請求頭、響應頭、實體類。
可是通常請求頭和響應頭只有一個,需求的實體類卻能夠是不少個,好比分頁查詢,假如如今拿到了一個Class對象,得到了這個對象的全類名,要怎麼判斷當前的這個Class指向的數據類型是方法參數列表中的實體類,又是哪一個實體類?
spring
給出方法
有一種方法:將請求頭和響應頭的判斷放在一開始,若是代碼通過了前兩輪判斷,則能夠肯定此時Class對象指向的不是請求頭也不是響應頭,又基於默認是實體類,就能夠直接進行json數據轉對象了,又因爲Class對象是與參數列表一一對應的,因此無所謂是哪一個實體類,你是Heroinfo 我就轉爲Heroinfo 你是PageInfo 我就轉Pageinfojson
方法存在風險
可是這是有風險的,萬一遇到一個方法就真存在第四種狀況呢,那麼一執行數據轉換就報錯了。
總不能窮舉整個項目中全部存在對象的路徑吧?真就窮舉全部狀況,而後判斷此時Class對象指向的不是項目中的對象時中止方法並輸出提示?那得累死啊數組
逆向思考
那麼反過來,假如已經肯定Class指向的是一個實體類了,咱們要作的就是使用自定義的工具類或者第三方jar包將請求頭中的數據存入該實體類的一個對象中。
因爲這種實體類多是任意一種數據類型,那麼能夠考慮給object,也能夠考慮給泛型 T
函數
若是給的是泛型 T 那麼此時baseServlet能夠設置爲泛型類。
工具
緊接着就要求各個子Servlet在繼承時說明本身的泛型類型,而各個子Servlet在繼承時說明的泛型類型就是各個子Servlet中各類方法的參數表中實際用到的數據類型。
this
這樣經過對 「json數據轉存到對象」的操做中泛型的設置,就能要求各個子Servlet在編寫時給出本身須要用的實體類,此時在對Class對象進行循環判斷時,就有一個實體類的範圍了——各個子servlet在繼承時說明的泛型類型3d
因而如今問題就從 「如何判斷Class對像指向的是實體類對象」轉爲「如何判斷class對象指向的是一個肯定範圍內的實體類對象」 前者那除非窮舉整個項目中全部實體對象,不然作不到,然後者就直接根據當前servlet使用的實體類對象進行判斷,就能夠作到。
因爲當前servlet使用的實體類對象都由於父類爲泛型類的緣由在繼承父類時都進行了聲明,所以咱們只須要想辦法取到各個子servlet在繼承時聲明的數據類型便可。
①設置父類爲泛型類
②聲明一個Class對象 用於儲存當前servlet類聲明的泛型類型
實際應該聲明一個Class對象數組,由於servlet類聲明的泛型類型能夠是多個,如今是由於已經知道servlet類只聲明瞭一個類型,所以偷懶了
③建立一個構造函數,在裏面編寫獲取servlet聲明的泛型的代碼
也能夠建立一個非靜態代碼塊,總之要求獲取servlet聲明的泛型的代碼要在整個servlet類執行時就執行
④上圖代碼執行完畢後就能得到servlet在聲明泛型時的具體類型,此時就能對Class對的全類名進行一個斷定,若是相同,說明此時Class對指向的就是一個實體類而不是別的東西,此時就能實現條件都不知足時提示「你這方法須要的參數既不是請求頭又不是響應頭還不是實體類,我這個servlet封裝搞不定這種複雜狀況,方法中止」
下面說明構造函數中那一長串代碼的具體實現,以實現功能爲順序說明
①this.getClass()
要記住代碼寫在父類中,但實際上是在子類執行,所以這個代碼獲取到的Class對象指向當前子類servlet
②this.getClass().getGenericSuperclass()
getGenericSuperclass 返回直接繼承的父類(包含泛型參數)
這裏使用了一個新的Class對象的方法——getGenericSuperclass()
語法 | 說明 |
---|---|
Class對象 . getGenericSuperclass() | 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的直接超類的 Type,返回的是一個Type |
Class對象 . getGenericInterfaces() | 沒有使用,但由於容易和上面的方法一塊兒談出來,提一下,這是返回當前實體實現的全部接口的Type,返回的是一個Type[] |
代碼:
輸出結果:直觀表現就是獲取到當前子類Servlet在繼承父類時extends後面寫的一長串信息,是父類的全類名+<聲明的數據類型的全類名>
如今咱們已經得到了一個能代表當前servlet類容許傳入的數據類型的信息(也即內部全部方法須要的數據類型) 這是一個Type格式的數據
此時咱們但願獲取到<>裏的全部信息
一種思路是直接將其以字符串格式輸出,而後進行字符串截取,可是這未免太不專業
另外一種思路就是將這個Type類型轉爲Class對象 這個Class對象想來應該和<>裏的數據有關
咱們能夠嘗試將Type類直接轉爲Class,由於Type實際上是一個接口,Class是它的實現類,這步操做屬於向下造型,是容許的,編譯時沒有報錯,可是容許時報錯了。
報錯:sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
這說明此時的Type類型其實不是java.lang包下的Type 而是Type這個接口下的一個實現類ParameterizedTypeImpl
所以此時的Type不容許直接強轉爲另外一個包下的實現類Class,咱們要先把當前這個實現類轉回頂層的Type格式,再轉成Class
並且怎麼想也不太對,如今的Type獲取到的數據是一長串數據,應該有一個方法能從中取出<>裏的數據,Class對象也應該是直接對應<>裏的數據,哪能將那麼長一串數據直接轉爲Class呢
③(ParameterizedType)this.getClass().getGenericSuperclass()
這步強轉操做是將sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl類強轉爲java.lang.reflect.ParameterizedType
ParameterizedType 是一個接口 底下有一個實現類爲sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 因此這屬於實現類轉回父類接口的類型 天然沒問題
而且此時就可使用這個接口的方法,這個接口只有三個方法
④((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()
這裏要使用接口類的第一種方法
語法 | 說明 |
---|---|
ParameterizedType對象 . getActualTypeArguments() | 返回一個表示此類型的實際類型參數的Type對象數組 |
getActualTypeArguments() 這個方法的說明有點難理解
其實就是將java.lang.reflect.ParameterizedType轉爲java.lang.reflect.Type
下面用一個小例子說明咱們拿到的是什麼:
如今父類定義了三個泛型
子類繼承父類而且聲明瞭三個泛型
此時代碼以下:
結果以下:
能夠看到這個方法返回的Type數組中的元素其實就是一個個Class對象,分別指向子類聲明的對象
這個數組中的元素就是咱們須要的Class對象——分別指向<>中聲明的數據類型
⑤(Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]
最後只須要從取到的數組中取出值,再強制轉換爲Class對象便可,將java.lang.reflect.Type轉爲java.lang.Class
這裏的[0]是由於這個數組中只有一個元素,放在索引爲0的位置,要從中取出賦值給設置好的Class對象,而後拿去作判斷