- 動(dòng)態(tài)類(lèi)型和靜態(tài)類(lèi)型:語(yǔ)言類(lèi)型是 運(yùn)行時(shí) 檢查,還是 編譯期 檢查
- 強(qiáng)類(lèi)型和弱類(lèi)型:為 不同類(lèi)型 的變量賦值時(shí),是否需要進(jìn)行 顯式的類(lèi)型轉(zhuǎn)換
- Java是 靜態(tài)的強(qiáng)類(lèi)型語(yǔ)言 ,但提供了類(lèi)似 反射 等機(jī)制,因此也具備了 部分 動(dòng)態(tài)類(lèi)型語(yǔ)言的能力
反射
- 反射機(jī)制是Java語(yǔ)言提供的一種基礎(chǔ)功能磺陡,賦予程序 在運(yùn)行時(shí)自省 的能力
- 通過(guò)反射可以 直接操作類(lèi)或者對(duì)象
- 獲取某個(gè)對(duì)象的類(lèi)定義
- 獲取類(lèi)聲明的屬性和方法
- 調(diào)用方法或者構(gòu)造函數(shù)
- 運(yùn)行時(shí)修改類(lèi)定義
setAccessible
- AccessibleObject.setAccessible(boolean flag):可以在 運(yùn)行時(shí) 修改成員的 訪問(wèn)限制
- setAccessible的應(yīng)用遍布在日常開(kāi)發(fā)、測(cè)試漠畜、依賴注入等框架中
- 在O/R Mapping框架中币他,為一個(gè)Java實(shí)體對(duì)象,運(yùn)行時(shí)自動(dòng)生成getter/setter方法
- 繞過(guò)API的訪問(wèn)控制憔狞,來(lái)調(diào)用內(nèi)部API
動(dòng)態(tài)代理
- 動(dòng)態(tài)代理是一種方便 運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建代理蝴悉、動(dòng)態(tài)處理代理方法調(diào)用 的機(jī)制
- 很多場(chǎng)景都是利用類(lèi)似的機(jī)制來(lái)實(shí)現(xiàn)的,例如用來(lái) 包裝RPC調(diào)用 和 AOP
- 實(shí)現(xiàn)動(dòng)態(tài)代理的方式
- JDK自身提供的動(dòng)態(tài)代理躯喇,主要利用 反射 機(jī)制
- 字節(jié)碼操作機(jī)制 辫封,類(lèi)似ASM、cglib(基于ASM)和Javassist
解決的問(wèn)題
- 動(dòng)態(tài)代理是一種 代理 機(jī)制廉丽,代理可以看作對(duì)調(diào)用目標(biāo)的 包裝 ,對(duì)目標(biāo)代碼的調(diào)用是通過(guò)代理完成的
- 通過(guò)代理可以讓 調(diào)用者和實(shí)現(xiàn)者解耦 妻味,例如RPC調(diào)用正压,框架內(nèi)部的尋址、序列化责球、反序列化等焦履,對(duì)調(diào)用者沒(méi)什么意義
發(fā)展歷程
- 靜態(tài)代理 -> 動(dòng)態(tài)代理
- 靜態(tài)代理:需要引入 額外的工作 拓劝,而這些工作與實(shí)際的業(yè)務(wù)邏輯沒(méi)有關(guān)系
- 古董技術(shù)RMI,需要rmic之類(lèi)的工具生成靜態(tài)stub等文件嘉裤,增加了很多繁瑣的準(zhǔn)備工作
- 動(dòng)態(tài)代理:相應(yīng)的stub等類(lèi)郑临,可以在運(yùn)行時(shí)生成,對(duì)應(yīng)的調(diào)用操作也是動(dòng)態(tài)生成的屑宠,極大地提高生產(chǎn)力
JDK Proxy + cglib
public class MyDynamicProxy {
public static void main(String[] args) {
Hello hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
// 構(gòu)造代理實(shí)例
Hello proxyHello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), hello.getClass().getInterfaces(), handler);
// 調(diào)用代理方法
proxyHello.sayHello();
// 輸出
// MyInvocationHandler Invoking HelloImpl#sayHello
// HelloImpl : Hello World
}
}
interface Hello {
void sayHello();
}
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println(getClass().getSimpleName() + " : Hello World");
}
}
@AllArgsConstructor
class MyInvocationHandler implements InvocationHandler {
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(getClass().getSimpleName() + " Invoking " + target.getClass().getSimpleName() + "#" + method.getName());
Object result = method.invoke(target, args);
return result;
}
}
- 實(shí)現(xiàn)InvocationHandler厢洞,添加 額外 邏輯
- 以Hello接口為紐帶,為被調(diào)用目標(biāo)構(gòu)建 代理對(duì)象 典奉,可以使用代理對(duì)象 間接 運(yùn)行調(diào)用目標(biāo)的邏輯
- 以 接口 為中心躺翻,相當(dāng)于添加了一種 對(duì)被調(diào)用者沒(méi)有太大意義的限制
- 另外實(shí)例化的是 Proxy對(duì)象 ,而不是真正的被調(diào)用類(lèi)型卫玖,可能會(huì)帶來(lái)各種不變和能力退化
- 如果 被調(diào)用者沒(méi)有實(shí)現(xiàn)接口 公你,可以通過(guò) cglib 來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理(克服了對(duì)接口的依賴)
- cglib動(dòng)態(tài)代理的方式:創(chuàng)建目標(biāo)類(lèi)的 子類(lèi) ,可以達(dá)到 近似使用被調(diào)用者本身 的效果
優(yōu)勢(shì)對(duì)比
JDK Proxy
-
最小化依賴關(guān)系 假瞬,簡(jiǎn)化開(kāi)發(fā)和維護(hù)陕靠,JDK本身的支持
-
JDK平滑升級(jí) ,而字節(jié)碼類(lèi)庫(kù)通常需要進(jìn)行 更新 以保證在新版Java上能夠使用
- 代碼實(shí)現(xiàn)簡(jiǎn)單
cglib
-
侵入性更小脱茉,JDK Proxy是基于 接口 的懦傍,而 限定被調(diào)用者實(shí)現(xiàn)特定接口 是有侵入性的實(shí)踐
-
只需操作關(guān)心的類(lèi) ,而不必為其它相關(guān)類(lèi)增加工作量
- 高性能
性能對(duì)比
- 在 主流 的JDK版本中芦劣,JDK Proxy在 典型場(chǎng)景 可以提供 對(duì)等的性能水平 粗俱,在數(shù)量級(jí)的差距并不是廣泛存在的
- 反射機(jī)制的性能在 現(xiàn)代 JDK中,已經(jīng)得到了 極大的改進(jìn)和優(yōu)化 虚吟,同時(shí)JDK的很多功能同樣使用了 ASM 進(jìn)行字節(jié)碼操作
- 在選型時(shí)寸认,性能并不是唯一考量,而 可靠性串慰、可維護(hù)性和編程工作量 才是更主要的考慮因素