Java基礎—反射與代理(新手向)
阿新 • • 發佈:2020-11-04
# 第1章 反射與代理
## 1.1 反射定義
### 1.1.1 大白話解釋一下反射
一般情況下,需要一個功能的前提是遇到了某個問題,這裡先列舉一些問題,然後再通過反射是如何解決了這些問題,來引出反射的定義。
普通開發人員工作中最常見的問題:需要生成代理物件(不清楚代理模式的話,可以簡單理解為需要將一個類,在不改變這個類的程式碼的基礎上,要對這個類的功能新增新的邏輯)
解決方式:將需要加強的類,利用**反射**載入之後,與補充的邏輯進行融合,產生一個新的物件,這個物件就是代理物件,即具備原有類及新邏輯的“增強後的類”(比如 Man 類裡面有個 eat() 方法,我們希望執行 eat() 方法前後分別執行洗手、洗碗邏輯,而我們又不能直接去修改eat()方法)
上面是用簡潔的方式給出了關於反射的最直白的解釋,下面給出較為**專業、全面**的解釋:
### 1.1.2 反射是什麼?
反射(Reflection)是 Java 程式開發語言的特徵之一,它允許執行中的 Java 程式獲取自身的資訊,並且可以操作類或物件的內部屬性。
通過反射機制,可以在執行時訪問 Java 物件的屬性,方法,構造方法等
反射的應用場景:
開發通用框架 - 反射最重要的用途就是開發各種通用框架。很多框架(比如 Spring)都是配置化的(比如通過 XML 檔案配置 JavaBean、Filter 等),為了保證框架的通用性,它們可能需要根據配置檔案載入不同的物件或類,呼叫不同的方法,這個時候就必須用到反射——執行時動態載入需要載入的物件
動態代理 - 在切面程式設計(AOP)中,需要攔截特定的方法,通常,會選擇動態代理方式。這時,就需要反射技術來實現了
註解 - 註解本身僅僅是起到標記作用,它需要利用反射機制,根據註解標記去呼叫註解直譯器,執行行為。如果沒有反射機制,註解並不比註釋更有用
可擴充套件性功能 - 應用程式可以通過使用完全限定名稱建立可擴充套件性物件例項來使用外部的使用者定義類
## 1.2 反射與代理中涉及術語
為了理解下一節反射與代理的關係,這裡先介紹一下會涉及到的術語:
真實物件(被代理物件):就是最原始的類例項化產生的物件,未經過代理模式對其加工增強,比如上面所說的Man類的物件
代理物件:利用代理模式增強後的物件,比如SuperMan物件
動態代理類:可以理解為代理物件邏輯處理器,也可以理解為“增強”的邏輯所處的位置,需要傳入真實物件產生關聯的動態代理物件
InvocationHandler 介面:動態代理類需要實現這個介面,並且重寫 invoke() 方法,“增強”的邏輯就寫在invoke方法裡,每個代理類的例項都關聯到了一個 Handler,當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由 InvocationHandler 這個介面的 invoke 方法來進行呼叫
Proxy:代理類,用以為將動態代理物件傳入之後,產生真正代理物件
## 1.3 反射與代理的關係
如上所述,代理模式的主要作用產生代理物件從而實現增強後的方法,而反射作為 Java 所提供的一個特性,是實現代理模式的基礎。換言之,利用反射技術獲取和操作Java程式裡面的類,從而可以對這些類進行包裝及加工,產生出代理物件。
獲得實現類物件的代理物件:
2.1 呼叫 Proxy.newProxyInstance 來獲得一個動態的代理物件,其接收三個引數,三個引數所代表的含義分別是:
①一個 ClassLoader 物件,定義了由哪個 ClassLoader 物件來對生成的代理物件進行載入
②一個 Interface 物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了
③ 一個 InvocationHandler 的實現類物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個 InvocationHandler 的實現類物件上
3.獲得代理物件的類物件(這裡我們可以列印一下代理類的類物件的類名)
4.獲得代理類的所有方法(通過暴力反射 getDeclaredMethods()獲得)(將所有獲得的方法遍歷,輸出所有的方法名)
5.通過代理物件呼叫實現類的方法(並賦值),觸發我們的重點步驟: InvocationHandler 介面的實現類中的invoked()方法,從而執行實現類的方法(sout輸出結果即可)(這一步就是所謂的無侵入式編碼規則)
5.1呼叫 InvocationHandler 介面具體步驟如下:
在觸發了實現類的方法後,首先需要在 InvocationHandler 介面中傳入三個引數,分別是
① proxy: - 指代我們所代理的那個真實物件
② method: - 指代的是我們所要呼叫真實物件的某個方法的Method物件()
③ args: - 指代的是呼叫真實物件某個方法時接受的引數
之後會執行介面獨有的invoked()方法,傳入兩個引數,分別是真實的實現類物件和傳入的引數args,最後返回方法。
## 1.4 JDK動態代理和CGLIB動態代理的區別
代理方式,其實都是通過繼承真實物件(被代理物件)的類或者實現其所實現的介面之後,將增強的邏輯補充進去完成的。
JDK動態代理就是通過實現介面完成的,所以當一個類是通過實現介面產生的,就是用JDK動態代理
CGLIB動態代理是通過繼承類完成的,所以當一個類沒有實現介面,那隻能使用JDK動態代理
## 1.5 Reflection框架
Java裡面提供了反射獲取類的各個屬性及方法的類,但是前提是拿到該類之後才能獲取並進行相應的操作,而反射框架Reflections不但能獲取classpath下面的類,還能根據特定的註解進行獲取。
Reflections通過掃描classpath,索引元資料,並且允許在執行時查詢這些元資料。
使用Reflections可以很輕鬆的獲取以下元資料資訊:
1. 獲取某個型別的全部子類
2. 只要型別、構造器、方法,欄位上帶有特定註解,便能獲取帶有這個註解的全部資訊(型別、構造器、方法,欄位)
3. 獲取所有能匹配某個正則表示式的資源
4. 獲取所有帶有特定簽名的方法,包括引數,引數註解,返回型別
5. 獲取所有方法的名字
6. 獲取程式碼裡所有欄位、方法名、構造器的使用
## 1.6 JsonCat裡面對於的動態代理的使用
jsoncat專案連結:https://github.com/Snailclimb/jsoncat
![](https://img2020.cnblogs.com/blog/1736590/202011/1736590-20201104161331111-1115766391.png)
## 1.7 參考
https://blog.csdn.net/yaomingyang/article/details/80981004
https://zhuanlan.zhihu.com/p/60805342
https://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-i