最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java中的動(dòng)態(tài)代理(jdk和Cglib)實(shí)現(xiàn)詳解

 更新時(shí)間:2025年04月26日 11:29:33   作者:自律的kkk  
這篇文章主要介紹了java中的動(dòng)態(tài)代理(jdk和Cglib)的相關(guān)資料,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

動(dòng)態(tài)代理

一、JDK動(dòng)態(tài)代理

準(zhǔn)備

JDK動(dòng)態(tài)代理只能代理接口,所以我們需要先準(zhǔn)備一個(gè)接口,以及一個(gè)接口的實(shí)現(xiàn)類

// 需要代理的接口
public interface InvokeInterface {
    void test1(String test,int[] ints);
    String test3(String test);
}
//代理接口的實(shí)現(xiàn)類
public class InvokeBean implements InvokeInterface{
    public void test1(String test,int[] ints){
        System.out.println("代理實(shí)現(xiàn)方法1 :"+test + JSON.toJSON(ints));
    }
    public String test3(String test){
        System.out.println("代理實(shí)現(xiàn)方法1 :"+test);
        return test;
    }
}

有了代理的目標(biāo)對象了,我們自然還需要一個(gè)處理方法,畢竟我們需要實(shí)現(xiàn)額外的邏輯呀,不然代理干嘛?對吧,這個(gè)處理方法可不能亂寫,需要實(shí)現(xiàn)InvocationHandler接口,里面有個(gè)invoke方法,就是給我們自己寫邏輯的地方,我們就可以對方法執(zhí)行前、后填充自己的邏輯了

注意: 這個(gè)類實(shí)例化的時(shí)候傳了一個(gè)Object參數(shù),這個(gè)就是上面的實(shí)現(xiàn)類,為什么要傳進(jìn)來?因?yàn)橐粋€(gè)接口是可以有多個(gè)實(shí)現(xiàn)類的,你不傳一個(gè)具體的實(shí)現(xiàn)類,我怎么知道要執(zhí)行哪個(gè)實(shí)現(xiàn)類里面的方法呢?

public class DemoInvokeHandler implements InvocationHandler {
    private Object object;
    public DemoInvokeHandler(Object object){
        this.object=object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 所以可以在這里  方法執(zhí)行前寫邏輯
        System.out.println("方法執(zhí)行前執(zhí)行");
        // 這個(gè)就是我們原本的方法執(zhí)行
        Object invoke=method.invoke(object,args);
        // 所以可以在這里  方法執(zhí)行后寫邏輯
        System.out.println("方法執(zhí)行后執(zhí)行");
        return invoke;
    }
}

像以上這樣呢,整個(gè)接口中的方法都會(huì)被代理,如果我們只想代理某個(gè)方法呢?就需要對方法判斷一下如:

這里只是對方法名過濾了,還可以結(jié)合注解、參數(shù)等過濾

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("test1")){
            // 所以可以在這里  方法執(zhí)行前寫邏輯
            System.out.println("方法執(zhí)行前執(zhí)行");
            // 這個(gè)就是我們原本的方法執(zhí)行
            Object invoke=method.invoke(object,args);
            // 所以可以在這里  方法執(zhí)行后寫邏輯
            System.out.println("方法執(zhí)行后執(zhí)行");
            return invoke;
        }else {
            return method.invoke(object,args);
        }
    }

使用

以上我們的準(zhǔn)備工作就完成了,我們只需要在實(shí)際使用中,獲取代理對象,執(zhí)行代理對象即可,通過

Proxy.newProxyInstance獲取代理對象,有三個(gè)傳參:

  • 類加載器
  • 被代理的接口
  • 需要做的處理類(也就是實(shí)現(xiàn)了InvocationHandler的類)
// 獲取代理對象
InvokeInterface invokeBean = (InvokeInterface) Proxy.newProxyInstance(InvokeBean.class.getClassLoader(),
    new Class[]{InvokeInterface.class}, new DemoInvokeHandler(new InvokeBean()));
invokeBean.test1("test1",new int[]{1,2});
String test3 = invokeBean.test3("test3");
System.out.println(test3);

二、Cglib動(dòng)態(tài)代理

準(zhǔn)備

這個(gè)我們需要先導(dǎo)入依賴包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

Cglib和JDK實(shí)現(xiàn)起來差不多,但是Cglib代理的是類,所以我們需要先準(zhǔn)備個(gè)目標(biāo)類

public class CglibTarget {
    public void test(){
        System.out.println("test 本方法執(zhí)行");
    }
}

同樣的還需要自己的處理方法,這個(gè)要實(shí)現(xiàn)的接口就不一樣。是Callback接口,而該接口有幾種:

img

我們只看一下MethodInterceptorInvocationHandler兩種,其他畢竟用得少:

InvocationHandler:用法和JDK動(dòng)態(tài)代理一樣,參考JDK的即可

MethodInterceptor

注意: 是net.sf.cglib.proxy.MethodInterceptor包下的,同時(shí)這個(gè)處理類實(shí)例化的時(shí)候就不需要再傳參了

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 所以可以在這里  方法執(zhí)行前寫邏輯
        System.out.println("方法執(zhí)行前執(zhí)行");
        // 這個(gè)就是我們原本的方法執(zhí)行
        Object o1 = methodProxy.invokeSuper(o, objects);
        // 所以可以在這里  方法執(zhí)行后寫邏輯
        System.out.println("方法執(zhí)行后執(zhí)行");
        return o1;
    }
}

使用

Cglib的使用同樣是需要先生成一個(gè)代理對象,一般是靠Enhancer.create() 方法,但該方法有幾個(gè)重載,我們一個(gè)一個(gè)介紹

先是最簡單也是最常用的

Enhancer.create(Class type, Callback callback)

  • type: 需要代理的類
  • callback:處理方法類
// MethodInterceptor 方式
CglibTarget cglibTarget = (CglibTarget) Enhancer.create(CglibTarget.class, new CglibProxy());
cglibTarget.test();
// InvocationHandler 方式  與JDK一樣,不需要類加載器了
Interface cglibTarget = (Interface) Enhancer.create(Interface.class, new CglibProxy(new 實(shí)現(xiàn)類));
cglibTarget.test();

結(jié)果:

img

####Enhancer.create((Class superclass, Class[] interfaces, Callback callback))

  • superclass: 需要代理的類
  • interfaces:需要實(shí)現(xiàn)的接口
  • callback:處理方法類

這個(gè)就比上面那個(gè)更厲害了,上面那個(gè)只是代理原有類里面的方法,這個(gè)可以對代理原有的類幫他實(shí)現(xiàn)接口,并實(shí)現(xiàn)接口邏輯,這等于一個(gè)類憑空多了幾個(gè)方法出來,怎么用?

上述的目標(biāo)類不需要改,處理方法需要改一下,然后新建一個(gè)接口:

// 我們新建一個(gè)接口不需要任何實(shí)現(xiàn)類  
public interface TestTarget {
    public void test1();
}
// 修改處理方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    // 這里新增對上面接口里面方法的處理,其他的不需要變
    if(method.getName().equals("test1")){
        // 這里可以實(shí)現(xiàn)那個(gè)接口方法的邏輯 
        System.out.println("這里寫實(shí)現(xiàn)那個(gè)接口的邏輯方法");
        return "我是那個(gè)接口返回的";
    }else {
        // 所以可以在這里  方法執(zhí)行前寫邏輯
        System.out.println("方法執(zhí)行前執(zhí)行");
        // 這個(gè)就是我們原本的方法執(zhí)行
        Object o1 = methodProxy.invokeSuper(o, objects);
        // 所以可以在這里  方法執(zhí)行后寫邏輯
        System.out.println("方法執(zhí)行后執(zhí)行");
        return o1;
    }
}

調(diào)用:

// 先用接口生成器 對我們要實(shí)現(xiàn)的接口做一個(gè)處理
InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
// 然后我們就可以正常的傳參調(diào)用了 
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibProxy());
// 類中方法正常調(diào)用
o.test();
// 接口方法調(diào)用需要用反射 因?yàn)楫吘鼓墙涌诘姆椒ú淮嬖趯ο蠓椒ɡ锩?
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);

結(jié)果:

Enhancer. create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)

  • superclass: 需要代理的類
  • interfaces:需要實(shí)現(xiàn)的接口
  • filter: 這個(gè)就是用來選擇處理類的,畢竟處理類有多個(gè)了
  • callbacks:處理方法類數(shù)組(處理類可以有多個(gè))

為了演示效果,我們新建一個(gè)處理類CglibProxyOther,上面的都不需要變

public class CglibProxyOther implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(method.getName().equals("test1")){
            System.out.println("新建處理類的邏輯方法");
            return "我是那個(gè)新建處理類返回的";
        }else {
            // 所以可以在這里  方法執(zhí)行前寫邏輯
            System.out.println("新建處理類——方法執(zhí)行前執(zhí)行");
            // 這個(gè)就是我們原本的方法執(zhí)行
            Object o1 = methodProxy.invokeSuper(o, objects);
            // 所以可以在這里  方法執(zhí)行后寫邏輯
            System.out.println("新建處理類——方法執(zhí)行后執(zhí)行");
            return o1;
        }
    }
}

然后新增一個(gè)過濾選擇器filter

實(shí)現(xiàn)CallbackFilter接口即可,返回值是處理類數(shù)組的下標(biāo)(我這里用方面名稱來選擇,實(shí)際還可以用其他)

public class CglibFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        // test方法用下標(biāo)為0的處理類
        if(method.getName().equals("test")){
            return 0;
        }
        // 其他方法用下標(biāo)為1的處理類
        return 1;
    }
}

調(diào)用:

InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibFilter(),new Callback[]{new CglibProxy(),new CglibProxyOther()});
o.test();
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);

結(jié)果: 很明顯的看到test1方法走了第二個(gè)處理類

img

至此重載方法全介紹完畢!

三、總結(jié)對比

  • 拓展性:要是考慮使用角度Cglib無疑是更好的,因?yàn)镴DK只能代理接口
  • 原理:JDK代理是利用反射機(jī)制生成匿名類,調(diào)用也是通過反射來調(diào)用 Cglib是采用字節(jié)碼技術(shù),通過修改字節(jié)碼生成子類
  • 效率:JDK創(chuàng)建對象效率較高,但執(zhí)行較慢,Cglib創(chuàng)建對象效率低,但執(zhí)行較快
  • 局限性: Cglib需要額外導(dǎo)入第三方包,而Jdk代理不需要,但JDK局限于代理接口

到底用什么相信大家有選擇了,如果沒特殊需求,就直接JDK得了,有特殊需求不用Cglib,JDK能滿足嗎?至于效率,這種效率的差距都體現(xiàn)在一定量往上的程度,沒到這個(gè)量無需考慮太多!

到此這篇關(guān)于java中的動(dòng)態(tài)代理(jdk和Cglib)實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)java 動(dòng)態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA配置和啟動(dòng)maven項(xiàng)目詳細(xì)步驟

    IDEA配置和啟動(dòng)maven項(xiàng)目詳細(xì)步驟

    本文介紹了從SVN檢出Web項(xiàng)目并進(jìn)行Maven化、JDK和項(xiàng)目結(jié)構(gòu)配置、Spring和Tomcat環(huán)境搭建的詳細(xì)步驟,幫助讀者順利完成Java Web項(xiàng)目的開發(fā)環(huán)境搭建
    2025-10-10
  • idea設(shè)置回車換行縮進(jìn)空格數(shù)為4的實(shí)現(xiàn)方式

    idea設(shè)置回車換行縮進(jìn)空格數(shù)為4的實(shí)現(xiàn)方式

    文章主要講述了解決在設(shè)置里回車換行僅縮進(jìn)兩空格的問題,通過將源代碼設(shè)置從回車兩空格改為回車四空格,解決了作者習(xí)慣上的不適
    2026-04-04
  • Java中將MultipartFile和File互轉(zhuǎn)的方法詳解

    Java中將MultipartFile和File互轉(zhuǎn)的方法詳解

    我們在開發(fā)過程中經(jīng)常需要接收前端傳來的文件,通常需要處理MultipartFile格式的文件,今天來介紹一下MultipartFile和File怎么進(jìn)行優(yōu)雅的互轉(zhuǎn),需要的朋友可以參考下
    2023-10-10
  • Java代碼是如何被CPU狂飆起來的

    Java代碼是如何被CPU狂飆起來的

    無論是剛剛?cè)腴TJava的新手還是已經(jīng)工作了的老司機(jī),恐怕都不容易把Java代碼如何一步步被CPU執(zhí)行起來這個(gè)問題完全講清楚。本文就帶你詳細(xì)了解Java代碼到底是怎么運(yùn)行起來的。感興趣的同學(xué)可以參考閱讀
    2023-03-03
  • Spring框架十一種常見異常的解決方法匯總

    Spring框架十一種常見異常的解決方法匯總

    今天小編就為大家分享一篇關(guān)于Spring框架十一種常見異常的解決方法匯總,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java 生產(chǎn)者/消費(fèi)者問題實(shí)例詳解

    Java 生產(chǎn)者/消費(fèi)者問題實(shí)例詳解

    這篇文章主要實(shí)例分析了java中生產(chǎn)者消費(fèi)者問題的方法,需要的朋友可以可以參考下
    2017-04-04
  • 自定義Spring Security的身份驗(yàn)證失敗處理方法

    自定義Spring Security的身份驗(yàn)證失敗處理方法

    在本篇文章里小編給大家整理了一篇關(guān)于自定義Spring Security的身份驗(yàn)證失敗的處理方法,有需要的朋友們學(xué)習(xí)下。
    2019-05-05
  • 一文帶你搞懂Java中的遞歸

    一文帶你搞懂Java中的遞歸

    這篇文章主要為大家詳細(xì)介紹了Java中的遞歸的實(shí)現(xiàn)以及應(yīng)用,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-10-10
  • Java 其中翻轉(zhuǎn)字符串的實(shí)現(xiàn)方法

    Java 其中翻轉(zhuǎn)字符串的實(shí)現(xiàn)方法

    這篇文章主要介紹了Java 其中翻轉(zhuǎn)字符串的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2014-02-02
  • 全面分析Java文件上傳

    全面分析Java文件上傳

    本片文章給大家詳細(xì)分析了Java文件上傳的相關(guān)知識點(diǎn),以及相關(guān)代碼做了詳細(xì)分析,有興趣的朋友學(xué)習(xí)下。
    2018-02-02

最新評論

辉县市| 石狮市| 沙田区| 修水县| 航空| 永济市| 昭平县| 乌鲁木齐市| 赣州市| 大邑县| 榆社县| 阿坝| 丹东市| 巫山县| 潼关县| 镇平县| 北票市| 玉溪市| 阳曲县| 灵山县| 浠水县| 垦利县| 淮北市| 洛隆县| 上饶县| 万盛区| 闽侯县| 武乡县| 黄平县| 金山区| 龙岩市| 阿巴嘎旗| 杭州市| 梓潼县| 阿勒泰市| 长顺县| 马尔康县| 英山县| 福泉市| 天全县| 富平县|