深度解讀)
三條 Proxy 代理特點(diǎn)深度解讀第 1 條Proxy 代理實(shí)例實(shí)現(xiàn)了一個(gè)加 FeignClient 注解的遠(yuǎn)程調(diào)用接口這句話在說(shuō)什么你只寫了一個(gè)接口沒(méi)有實(shí)現(xiàn)類比如FeignClient(nameuser-service)publicinterfaceUserClient{GetMapping(/user/{id})UsergetUser(PathVariable(id)Longid);}Java 里接口是沒(méi)法直接new的因?yàn)榻涌跊](méi)有方法體。那 Spring 怎么讓你Autowired UserClient userClient拿到一個(gè)能用的對(duì)象答案就是JDK 動(dòng)態(tài)代理Spring 在運(yùn)行時(shí)憑空生成一個(gè)實(shí)現(xiàn)了 UserClient 接口的對(duì)象這個(gè)對(duì)象就是Proxy 代理實(shí)例。關(guān)鍵點(diǎn)它實(shí)現(xiàn)了接口但方法體是動(dòng)態(tài)的UserClientuserClient(UserClient)Proxy.newProxyInstance(classLoader,// 類加載器newClass[]{UserClient.class},// 要實(shí)現(xiàn)的接口newFeignInvocationHandler(target,dispatch)// 方法被調(diào)用時(shí)誰(shuí)來(lái)處理);這個(gè)代理對(duì)象在類型上確實(shí)實(shí)現(xiàn)了UserClient所以能賦給UserClient類型變量。但它里面沒(méi)有你寫的任何業(yè)務(wù)邏輯——所有方法調(diào)用都會(huì)被轉(zhuǎn)發(fā)給FeignInvocationHandler。這就是代理的含義它替接口做事情但事情的具體處理邏輯在別處Handler 里。一句話理解“接口負(fù)責(zé)聲明代理實(shí)例負(fù)責(zé)實(shí)現(xiàn)”——Proxy 是接口的影子實(shí)現(xiàn)讓你能像 new 一個(gè)普通類一樣使用接口。第 2 條Proxy 代理實(shí)例能在內(nèi)部進(jìn)行 HTTP 請(qǐng)求的封裝以及發(fā)送 HTTP 請(qǐng)求這句話在說(shuō)什么當(dāng)你調(diào)用userClient.getUser(100L)時(shí)這個(gè)調(diào)用沒(méi)有真的執(zhí)行任何 Java 邏輯而是被代理攔截轉(zhuǎn)成了發(fā)一個(gè) HTTP 請(qǐng)求。調(diào)用被FeignInvocationHandler.invoke()攔截后內(nèi)部做兩件事① 封裝 HTTP 請(qǐng)求把方法調(diào)用翻譯成 HTTP 報(bào)文// FeignInvocationHandler.invoke() 里MethodHandlerhandlerdispatch.get(method);// 找到這個(gè)方法對(duì)應(yīng)的處理器returnhandler.invoke(args);// 處理實(shí)際調(diào)用再往下SynchronousMethodHandler負(fù)責(zé)封裝請(qǐng)求publicObjectinvoke(Object[]argv){// 把方法參數(shù)填進(jìn)模板生成 Request(HTTP報(bào)文)RequestTemplatetemplatebuildTemplateFromArgs.create(argv);// 執(zhí)行請(qǐng)求ResponseresponseexecuteAndDecode(template);returnresponse;}buildTemplateFromArgs.create(argv)做的事就是封裝getUser(100L)→ 把100填進(jìn)模板占位符{id}→/user/100再拼上 header、body → 生成完整的Request② 發(fā)送 HTTP 請(qǐng)求真正把報(bào)文發(fā)出去// Client.execute() 真正通過(guò)底層 HTTP 客戶端發(fā)送Responseresponseclient.execute(request,options);這一步由feign.Client完成Spring Cloud 默認(rèn)用 Apache HttpClient 或 OkHttp 把報(bào)文發(fā)到遠(yuǎn)程服務(wù)。關(guān)鍵點(diǎn)“封裝 發(fā)送” 全在代理實(shí)例內(nèi)部完成對(duì)調(diào)用者完全透明。調(diào)用者以為自己在調(diào)方法實(shí)際代理幫你做了把 Java 方法調(diào)用翻譯成 HTTP 報(bào)文再發(fā)出去。一句話理解代理實(shí)例把方法調(diào)用翻譯成HTTP 請(qǐng)求并發(fā)送——這是它最核心的本領(lǐng)也是讓你不用寫 HTTP 報(bào)文的原因。第 3 條Proxy 代理實(shí)例能處理遠(yuǎn)程 HTTP 請(qǐng)求的響應(yīng)并且完成結(jié)果的解碼然后返回給調(diào)用者這句話在說(shuō)什么遠(yuǎn)程服務(wù)返回的是一段HTTP 響應(yīng)通常是 JSON 字符串而你的方法返回類型是User對(duì)象。代理實(shí)例要把字符串解碼成對(duì)象再返回給你。// Decoder.decode() 把響應(yīng)體轉(zhuǎn)成方法聲明的返回類型Objectresultdecoder.decode(response,returnType);// 返回給調(diào)用者returnresult;以SpringDecoder為例它內(nèi)部委托給 Spring 的HttpMessageConverter// 把 response 里的 JSON 字節(jié)流 → User 對(duì)象Useruser(User)converter.read(User.class,...);關(guān)鍵點(diǎn)從方法調(diào)用到拿到結(jié)果形成完整閉環(huán)調(diào)用者視角UseruseruserClient.getUser(100L);// 就這么一行代理內(nèi)部實(shí)際發(fā)生了getUser(100L) → 封裝HTTP請(qǐng)求(模板填充) → 發(fā)送HTTP請(qǐng)求 → 接收HTTP響應(yīng)(JSON字符串) → 解碼成User對(duì)象 → 返回給調(diào)用者調(diào)用者全程不知道 HTTP 的存在它拿到的就是一個(gè)普普通通的User對(duì)象——就像調(diào)用本地方法一樣自然。一句話理解代理實(shí)例負(fù)責(zé)收尾把遠(yuǎn)程返回的 JSON 解碼成對(duì)象原樣還給你讓遠(yuǎn)程調(diào)用偽裝成了一次普通的方法調(diào)用。三句話連起來(lái) Feign 的完整原理把三條串成一條流水線就正好回答了Feign 為什么能讓遠(yuǎn)程調(diào)用像本地調(diào)用一樣① Proxy 實(shí)現(xiàn)了 FeignClient 接口 → 讓你能假裝直接調(diào)用接口 ↓ ② Proxy 內(nèi)部封裝 發(fā)送 HTTP 請(qǐng)求 → 幫你把方法調(diào)用變成HTTP 報(bào)文發(fā)出去 ↓ ③ Proxy 解碼響應(yīng)并返回 → 把遠(yuǎn)程返回的 JSON 變成對(duì)象還給你一句話總結(jié)Proxy 代理實(shí)例 一個(gè)會(huì)翻譯的影子實(shí)現(xiàn)——調(diào)用接口方法①它幫你把方法翻譯成 HTTP 報(bào)文發(fā)出去②再把遠(yuǎn)程返回的 JSON 翻譯回對(duì)象還給你③。整個(gè)過(guò)程對(duì)調(diào)用者完全透明所以 Feign 能讓你不碰 HTTP 報(bào)文就完成遠(yuǎn)程調(diào)用。補(bǔ)充對(duì)應(yīng)到源碼的位置三條特點(diǎn)對(duì)應(yīng)源碼類歸屬模塊① 實(shí)現(xiàn)接口生成代理FeignClientFactoryBean/Proxy.newProxyInstancespring-cloud-openfeign-core① 攔截調(diào)用FeignInvocationHandlerfeign-core② 封裝 發(fā)送SynchronousMethodHandler/BuildTemplateByResolvingArgs/Clientfeign-core③ 解碼返回DecoderSpringDecoderspring-cloud-openfeign-core