第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.6 JsonCat里面對於的動態代理的使用
jsoncat項目鏈接:https://github.com/Snailclimb/jsoncat
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-it-useful