摘要:總結(jié)動(dòng)態(tài)代理的相關(guān)原理已經(jīng)講解完畢,接下來(lái)讓我們回答以下幾個(gè)思考題。
【干貨點(diǎn)】 此處是【好好面試】系列文的第12篇文章。
文章目標(biāo)主要是通過(guò)原理剖析的方式解答Aop動(dòng)態(tài)代理的面試熱點(diǎn)問(wèn)題,通過(guò)一步步提出問(wèn)題和了解原理的方式,我們可以記得更深更牢,進(jìn)而解決被面試官卡住喉嚨的情況。
問(wèn)題如下
SpringBoot默認(rèn)代理類型是什么
為什么不用靜態(tài)代理
JDK動(dòng)態(tài)代理原理
CGLIB動(dòng)態(tài)代理原理
JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的區(qū)別
為什么CGLIB不能像JDK代理那樣,直接使用反射觸發(fā)目標(biāo)函數(shù)
為什么CGLIB代理可以直接對(duì)類進(jìn)行代理,而JDK代理卻一定要實(shí)現(xiàn)接口
大前提該文章是必須要懂的SpringAop系列的最后一篇文章,第一篇文章是你必須要懂的Spring-Aop之應(yīng)用篇,第二篇文章是你必須要懂的Spring-Aop之源碼跟蹤分析Aop,最后一篇文章我們將會(huì)揭露Aop的原理,也就是動(dòng)態(tài)代理。希望看完這最后一篇文章,大家對(duì)SpringAop都有一個(gè)全面的掌握,不管面試官如何問(wèn),都可以屌回去 ━━( ̄ー ̄*|||━━
溫馨提示:前面沒(méi)看的最好點(diǎn)擊瀏覽下?。?!說(shuō)個(gè)坑爹的事
大家都知道,我有個(gè)習(xí)慣,在動(dòng)手寫(xiě)一篇文章之前會(huì)先將該文章相關(guān)的資料仔細(xì)琢磨一遍,然后再結(jié)合源碼再調(diào)試一遍,結(jié)果,說(shuō)好的
看源碼也確實(shí)是
源碼確實(shí)有進(jìn)行了是否是接口的判斷,但是問(wèn)題來(lái)了,我調(diào)試的時(shí)候發(fā)現(xiàn)無(wú)論代理類是否有接口,最終都會(huì)被強(qiáng)制使用CGLIB代理,沒(méi)辦法,只能翻看SpringBoot的相關(guān)文檔,最終發(fā)現(xiàn)原來(lái)SpringBoot從2.0開(kāi)始就默認(rèn)使用Cglib代理了,好家伙,怪不得我調(diào)試半天找不到原因。
那么如何解決呢?肯定是通過(guò)配置啦,按照如下配置即可
在application.properties文件中配置 spring.aop.proxy-target-class=false
即可。
【劃重點(diǎn)】 曾經(jīng)遇見(jiàn)過(guò)面試官問(wèn),SpringBoot默認(rèn)代理類型是什么?看完該篇文章,我們就可以果斷的回答是Cglib代理了。通過(guò)調(diào)試代碼發(fā)現(xiàn)的規(guī)則,我想我這輩子都不會(huì)忘記這個(gè)默認(rèn)規(guī)則。動(dòng)態(tài)代理原理剖析 什么是代理
簡(jiǎn)單來(lái)說(shuō),就是在運(yùn)行的時(shí)候?yàn)槟繕?biāo)類動(dòng)態(tài)生成代理類,而在操作的時(shí)候都是操作代理類,代理模式有個(gè)顯而易見(jiàn)的好處,那便是可以在不改變對(duì)象方法的情況下對(duì)方法進(jìn)行增強(qiáng)。試想下,我們?cè)谀惚仨氁腟pring-Aop之應(yīng)用篇有提到使用Aop來(lái)做權(quán)限認(rèn)證,如果不用Aop,那么我們就必須要為所有需要權(quán)限認(rèn)證的方法都加上權(quán)限認(rèn)證代碼,聽(tīng)起來(lái)就覺(jué)得蛋疼,你覺(jué)得對(duì)不對(duì)?
為什么不用靜態(tài)代理靜態(tài)代理類不是說(shuō)不可以用,如果只有一個(gè)類需要被代理,那么自然可以用,如
這是在你必須要懂的Spring-Aop之應(yīng)用篇使用的一個(gè)例子類,該類的作用只是打印出我要買(mǎi)東西。
代理類如下
可以看到這個(gè)BuyProxy代理類只是塞了一個(gè)IBuyServcie接口進(jìn)行,而且自身也實(shí)現(xiàn)了接口IBuyService,而在buyItem方法被調(diào)用的時(shí)候會(huì)先做自己的操作,再調(diào)用塞進(jìn)去的接口的buyItem方法。
測(cè)試類很簡(jiǎn)單,如下
運(yùn)行后很自然而然的打印出
靜態(tài)代理就是簡(jiǎn)單,但是弊端也很明顯,如果有多個(gè)類都需要同樣的代理,都實(shí)現(xiàn)了同樣的接口,那么如果使用靜態(tài)代理的話,我們就要構(gòu)造多個(gè)Proxy類,就會(huì)造成類爆炸。
而使用了Aop后,也就是動(dòng)態(tài)代理后,便可以一次性解決該問(wèn)題了,具體可以看你必須要懂的Spring-Aop之應(yīng)用篇中的操作方法。
這里給出一個(gè)JDK動(dòng)態(tài)代理的demo
首先給出一個(gè)簡(jiǎn)單的業(yè)務(wù)類,Hello類和接口
真正實(shí)現(xiàn)了類的代理功能的其實(shí)就是這個(gè)實(shí)現(xiàn)了接口InvocationHandler的JdkProxy類
我們可以看到其中必須實(shí)現(xiàn)的方法是invoke,可以看到invoke方法的參數(shù)帶有Method對(duì)象,這個(gè)就是我們的目標(biāo)Method,現(xiàn)在我們的目的就是要在這個(gè)Method在被調(diào)用前后實(shí)現(xiàn)我們的業(yè)務(wù),可以看到在method.invoke反調(diào)前后實(shí)現(xiàn)了before和after業(yè)務(wù)。
這里再給出一個(gè)Main測(cè)試類,作用是取得Hello的代理類,然后調(diào)用其中的say方法。
運(yùn)行結(jié)果如下
原理很簡(jiǎn)單 在JdkProxyMain中hello調(diào)用say的時(shí)候,由于Hello已經(jīng)被“代理”了,所以在調(diào)用say函數(shù)的時(shí)候其實(shí)是調(diào)用JdkProxy類中的invoke函數(shù),而在invoke函數(shù)中先是實(shí)現(xiàn)了before函數(shù)才實(shí)現(xiàn)Object result = method.invoke(target, args),這一句其實(shí)是調(diào)用say函數(shù),而后才實(shí)現(xiàn)after函數(shù),于是這樣就可以不必在改動(dòng)目標(biāo)類的前提下實(shí)現(xiàn)代理了,并且不會(huì)像靜態(tài)代理那樣導(dǎo)致類爆炸。
CGLIB動(dòng)態(tài)代理原理先給出一個(gè)Cglib動(dòng)態(tài)代理的demo
【思考題一】為什么CGLIB代理可以直接對(duì)類進(jìn)行代理,而JDK代理卻一定要實(shí)現(xiàn)接口呢?答案見(jiàn)問(wèn)末?。?!
核心類是實(shí)現(xiàn)了MethodInterceptor的CGlibProxy類
可以看到其中實(shí)現(xiàn)了方法intercept,先是在目標(biāo)函數(shù)被調(diào)用前實(shí)現(xiàn)自己的業(yè)務(wù),比如before()和after(),之后再通過(guò) proxy.invokeSuper(obj, args) 觸發(fā)目標(biāo)函數(shù)。
【思考題二】為什么這里不像JDK代理那樣,直接使用反射[method.invoke(target, args)]觸發(fā)目標(biāo)函數(shù)?答案見(jiàn)問(wèn)末?。?!
最后給出入口類
最后給出運(yùn)行類,運(yùn)行類如下
可以看到運(yùn)行結(jié)果
原理很簡(jiǎn)單 在CglibProxyMain中hello調(diào)用say的時(shí)候,由于Hello已經(jīng)被“代理”了,所以在調(diào)用say函數(shù)的時(shí)候其實(shí)是調(diào)用CGlibProxy類中的intercept函數(shù)。
總結(jié)動(dòng)態(tài)代理的相關(guān)原理已經(jīng)講解完畢,接下來(lái)讓我們回答以下幾個(gè)思考題。
「思考解惑一」為什么CGLIB代理可以直接對(duì)類進(jìn)行代理,而JDK代理卻一定要實(shí)現(xiàn)接口呢?
可以從我們上面的例子看出,在JdkProxy類中取得代理類的方式是
(T)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this)
而在CGlibProxy類中取得代理類的方式是
(T) Enhancer.create(cls, this)
兩種取得代理類的方式不同,導(dǎo)致了一個(gè)需要實(shí)現(xiàn)接口,一個(gè)不需要。
「思考解惑二」為什么這里不像JDK代理那樣,直接使用反射[method.invoke(target, args)]觸發(fā)目標(biāo)函數(shù)?
首先要進(jìn)行反射觸發(fā)函數(shù),要取得對(duì)應(yīng)的method,以及該method所屬對(duì)象,也就是target,再次是args方法參數(shù),而我們看下調(diào)試界面
可以從界面看到,目標(biāo)對(duì)象obj并不是Hello對(duì)象,二是被CGLIB代理過(guò)的對(duì)象,因此無(wú)法像JDK代理那樣直接通過(guò)反射搞定。
最后的最后鑒于很多朋友都來(lái)找我聊《好好面試》相關(guān)的問(wèn)題,本人也比較忙,無(wú)法一一應(yīng)答,為此在此創(chuàng)建了一個(gè)微信群,有興趣的可以加入,只聊面試和技術(shù)相關(guān)。
如果過(guò)期了,可以在關(guān)注公眾號(hào),在公眾號(hào)上喊我一聲,說(shuō)要加群即可。
[好好面試] 系列文推薦:
你必須要懂的Spring-Aop之源碼跟蹤分析Aop
你必須要懂的Spring-Aop之應(yīng)用篇
你所不知道的HelloWorld背后的原理
連引用都答不上,憑什么說(shuō)你是Java服務(wù)端開(kāi)發(fā)
你是否了解Spring中bean的生命周期呢?
開(kāi)發(fā)必學(xué),io&nio
你所不知道的HelloWorld背后的原理
如何基于spring動(dòng)態(tài)注冊(cè)bean
拓展spring組件之自定義標(biāo)簽
基于spring實(shí)現(xiàn)事件驅(qū)動(dòng)
Java日常干貨
Java&Spring系列筆記
[gomicro微服務(wù)] 系列文推薦:
go微服務(wù)系列之一
go微服務(wù)系列之二
go微服務(wù)系列之三
go微服務(wù)系列之四
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75802.html
摘要:用戶態(tài)不能干擾內(nèi)核態(tài)所以指令就有兩種特權(quán)指令和非特權(quán)指令不同的狀態(tài)對(duì)應(yīng)不同的指令。非特權(quán)指令所有程序均可直接使用。用戶態(tài)常態(tài)目態(tài)執(zhí)行非特權(quán)指令。 這是我今年從三月份開(kāi)始,主要的大廠面試經(jīng)過(guò),有些企業(yè)面試的還沒(méi)來(lái)得及整理,可能有些沒(méi)有帶答案就發(fā)出來(lái)了,還請(qǐng)各位先思考如果是你怎么回答面試官?這篇文章會(huì)持續(xù)更新,請(qǐng)各位持續(xù)關(guān)注,希望對(duì)你有所幫助! 面試清單 平安產(chǎn)險(xiǎn) 飛豬 上汽大通 浩鯨科...
摘要:思考之所以會(huì)選擇為切入點(diǎn),是因?yàn)橥ㄟ^(guò)命名可以看出這是用來(lái)構(gòu)建代理強(qiáng)化對(duì)象的地方,并且由于是先將目標(biāo)類加載到內(nèi)存中,之后通過(guò)修改字節(jié)碼生成目標(biāo)類的子類,因此我猜測(cè)強(qiáng)化是在目標(biāo)類實(shí)例化后觸發(fā)的時(shí)候進(jìn)行的。 【干貨點(diǎn)】 此處是【好好面試】系列文的第11篇文章。看完該篇文章,你就可以了解Spring中Aop的相關(guān)使用和原理,并且能夠輕松解答Aop相關(guān)的面試問(wèn)題。更重要的是,很多人其實(shí)一看源碼就...
摘要:干貨點(diǎn)此處是好好面試系列文的第篇文章。而這也是出現(xiàn)的原因,沒(méi)錯(cuò),就是被設(shè)計(jì)出來(lái)彌補(bǔ)短板的。運(yùn)行結(jié)果如下運(yùn)行結(jié)果可想而知,的通過(guò)驗(yàn)證,的失敗。 【干貨點(diǎn)】此處是【好好面試】系列文的第10篇文章??赐暝撈恼?,你就可以了解Spring中Aop的相關(guān)使用和原理,并且能夠輕松解答Aop相關(guān)的面試問(wèn)題。 在實(shí)際研發(fā)中,Spring是我們經(jīng)常會(huì)使用的框架,畢竟它們太火了,也因此Spring相關(guān)的知...
摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開(kāi)發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過(guò)的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛(ài)情萌芽的模樣…… Java 進(jìn)階面試問(wèn)題列表 -...
閱讀 2013·2021-09-22 16:05
閱讀 9336·2021-09-22 15:03
閱讀 2894·2019-08-30 15:53
閱讀 1707·2019-08-29 11:15
閱讀 917·2019-08-26 13:52
閱讀 2361·2019-08-26 11:32
閱讀 1811·2019-08-26 10:38
閱讀 2576·2019-08-23 17:19