人人妻人人澡人人爽人人精品av_精品乱码一区内射人妻无码_老司机午夜福利视频_精品成品国色天香摄像头_99精品福利国产在线导航_野花社区在线观看视频_大地资源在线影视播放_东北高大肥胖丰满熟女_金门瓶马车内剧烈运动

首頁>國(guó)內(nèi) > 正文

面試官:為什么JDK動(dòng)態(tài)代理只能代理接口?

2023-07-05 10:30:54來源:Java技術(shù)指北

哈嘍,大家好,我是了不起。如題,這個(gè)問題應(yīng)該面試??碱},當(dāng)你遇到這個(gè)問題時(shí),如果你能回答出來JDK動(dòng)態(tài)代理的原理,然后引申Cglib 動(dòng)態(tài)代理,那么這個(gè)面試官一定會(huì)對(duì)你刮目相看。


(相關(guān)資料圖)

在Java中,動(dòng)態(tài)代理是一種機(jī)制,允許在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建代理對(duì)象來代替某個(gè)實(shí)際對(duì)象,從而在其前后執(zhí)行額外的邏輯。

為什么JDK動(dòng)態(tài)代理只能代理接口實(shí)現(xiàn)類,原因是JDK動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的。

當(dāng)你使用Proxy類創(chuàng)建代理對(duì)象時(shí),你需要指定一個(gè)接口列表來表示代理對(duì)象所應(yīng)該實(shí)現(xiàn)的接口,這些接口就成為代理對(duì)象的類型。

具體來說,代理對(duì)象的方法調(diào)用會(huì)被轉(zhuǎn)發(fā)到實(shí)現(xiàn)InvocationHandler接口的類中的invoke()方法。這個(gè)invoke()方法接受三個(gè)參數(shù):代理對(duì)象本身、被調(diào)用的方法對(duì)象和方法的參數(shù)數(shù)組。invoke()方法需要返回被代理方法調(diào)用的結(jié)果。

由于代理對(duì)象的類型是由接口列表決定的,因此只有實(shí)現(xiàn)了接口的類才能被代理。如果你想代理一個(gè)類而不是一個(gè)接口,你需要使用其他的代理技術(shù),比如CGLIB。

1、JDK動(dòng)態(tài)代理代碼實(shí)例

下面是一個(gè)簡(jiǎn)單的示例代碼,展示了如何使用JDK動(dòng)態(tài)代理來創(chuàng)建代理對(duì)象。

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyDemo {    public static void main(String[] args) {        RealObject real = new RealObject();        InvocationHandler handler = new DynamicProxy(real);        // 創(chuàng)建代理對(duì)象        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(                MyInterface.class.getClassLoader(),                new Class[] { MyInterface.class },                handler);        // 調(diào)用代理對(duì)象的方法        proxy.doSomething();    }}interface MyInterface {    void doSomething();}class RealObject implements MyInterface {    public void doSomething() {        System.out.println("RealObject doSomething");    }}class DynamicProxy implements InvocationHandler {    private Object target;    public DynamicProxy(Object target) {        this.target = target;    }    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        System.out.println("Before method invocation");        Object result = method.invoke(target, args);        System.out.println("After method invocation");        return result;    }}

在上面的代碼中,RealObject實(shí)現(xiàn)了MyInterface接口,它是我們要代理的實(shí)際對(duì)象。DynamicProxy類實(shí)現(xiàn)了InvocationHandler接口,并在invoke()方法中添加了額外的邏輯,用于在代理對(duì)象方法調(diào)用前后執(zhí)行。

在main()方法中,我們使用Proxy.newProxyInstance()方法創(chuàng)建代理對(duì)象。我們指定了MyInterface接口作為代理對(duì)象類型,并將DynamicProxy對(duì)象作為代理對(duì)象的InvocationHandler。

最后,我們調(diào)用代理對(duì)象的doSomething()方法,并觀察控制臺(tái)輸出的結(jié)果。

需要注意的是,代理對(duì)象的方法調(diào)用都會(huì)被轉(zhuǎn)發(fā)到DynamicProxy類的invoke()方法中進(jìn)行處理,因此在這個(gè)示例中,實(shí)際的RealObject對(duì)象的doSomething()方法的執(zhí)行是在invoke()方法中通過反射進(jìn)行的。

總結(jié)一下,JDK動(dòng)態(tài)代理只能代理接口實(shí)現(xiàn)類,原因是JDK動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,代理對(duì)象的類型由接口列表決定。如果你想代理一個(gè)類而不是一個(gè)接口,你需要使用其他的代理技術(shù),比如CGLIB。

2、Cglib 代碼演示

以下是CGLIB代理的示例代碼。

import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CGLIBProxyDemo {    public static void main(String[] args) {        RealObject real = new RealObject();        MethodInterceptor handler = new CGLIBProxy(real);        // 創(chuàng)建代理對(duì)象        RealObject proxy = (RealObject) Enhancer.create(                RealObject.class,                handler);        // 調(diào)用代理對(duì)象的方法        proxy.doSomething();    }}class CGLIBProxy implements MethodInterceptor {    private Object target;    public CGLIBProxy(Object target) {        this.target = target;    }    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        System.out.println("Before method invocation");        Object result = proxy.invoke(target, args);        System.out.println("After method invocation");        return result;    }}

在上面的示例中,我們使用CGLIB的Enhancer類和MethodInterceptor接口來創(chuàng)建代理對(duì)象。RealObject類不再需要實(shí)現(xiàn)接口,而是直接作為代理對(duì)象的類型。在CGLIBProxy類中,我們實(shí)現(xiàn)了MethodInterceptor接口,并在intercept()方法中添加了額外的邏輯。

在main()方法中,我們使用Enhancer.create()方法創(chuàng)建代理對(duì)象。我們指定了RealObject類作為代理對(duì)象類型,并將CGLIBProxy對(duì)象作為代理對(duì)象的MethodInterceptor。最后,我們調(diào)用代理對(duì)象的doSomething()方法,并觀察控制臺(tái)輸出的結(jié)果。

需要注意的是,CGLIB代理使用字節(jié)碼技術(shù)來生成代理對(duì)象,因此它的效率比JDK動(dòng)態(tài)代理要高,但是它也需要額外的庫(kù)依賴。

3、兩者優(yōu)缺點(diǎn)

JDK動(dòng)態(tài)代理和CGLIB代理都有它們自己的優(yōu)缺點(diǎn)。

JDK動(dòng)態(tài)代理的優(yōu)點(diǎn):

JDK動(dòng)態(tài)代理是Java標(biāo)準(zhǔn)庫(kù)的一部分,因此它不需要引入任何外部依賴。JDK動(dòng)態(tài)代理只需要實(shí)現(xiàn)接口即可生成代理對(duì)象,不需要改變?cè)蓄惖慕Y(jié)構(gòu)。由于JDK動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,因此它更適合用于代理接口實(shí)現(xiàn)類的場(chǎng)景。

JDK動(dòng)態(tài)代理的缺點(diǎn):

JDK動(dòng)態(tài)代理只能代理實(shí)現(xiàn)了接口的類,無法代理沒有實(shí)現(xiàn)接口的類。JDK動(dòng)態(tài)代理在生成代理對(duì)象時(shí),需要使用反射機(jī)制,因此它的效率相對(duì)較低。

CGLIB代理的優(yōu)點(diǎn):

CGLIB代理是基于字節(jié)碼技術(shù)實(shí)現(xiàn)的,因此它的效率比JDK動(dòng)態(tài)代理更高。CGLIB代理可以代理沒有實(shí)現(xiàn)接口的類。

CGLIB代理的缺點(diǎn):

CGLIB代理需要引入外部依賴。CGLIB代理在生成代理對(duì)象時(shí),需要改變?cè)蓄惖慕Y(jié)構(gòu),因此它可能會(huì)引起一些問題,例如無法代理final類或final方法等問題。

綜上所述,JDK動(dòng)態(tài)代理適用于代理接口實(shí)現(xiàn)類的場(chǎng)景,而CGLIB代理適用于代理沒有實(shí)現(xiàn)接口的類的場(chǎng)景。如果你需要代理接口實(shí)現(xiàn)類,而且不想引入額外的依賴,那么JDK動(dòng)態(tài)代理是一個(gè)不錯(cuò)的選擇;如果你需要代理沒有實(shí)現(xiàn)接口的類,那么CGLIB代理可能更適合你的需求。

關(guān)鍵詞:

相關(guān)新聞

Copyright 2015-2020   三好網(wǎng)  版權(quán)所有 聯(lián)系郵箱:435 22 [email protected]  備案號(hào): 京ICP備2022022245號(hào)-21