首頁技術(shù)文章正文

兩種常用的動態(tài)代理方式

更新時間:2020-07-17 來源:黑馬程序員 瀏覽量:

回顧OOP(面向?qū)ο缶幊?

·三大特征:封裝、繼承和多態(tài)

·比如說,有Dog類、Cat類、Horse類,它們都有eat方法,run方法,按照OOP的編程思想,那么我們可以抽象出父類Animal,在父類中放置相同的屬性或者方法,這樣來避免多子類中重復(fù)的代碼。

·一切皆對象,很牛逼了,其實它有缺陷!


java動態(tài)代理


OOP是縱向抽取和繼承體系,OOP很多場合都能夠解決我們的問題【代碼重復(fù)問題】,但是有一些場合,也有它處理不了的需要被解決的大量的代碼重復(fù)問題。

什么是動態(tài)代理?

動態(tài)代理就是,在程序運行期,創(chuàng)建目標(biāo)對象的代理對象,并對目標(biāo)對象中的方法進(jìn)行功能性增強(qiáng)的一種技術(shù)。在生成代理對象的過程中,目標(biāo)對象不變,代理對象中的方法是目標(biāo)對象方法的增強(qiáng)方法??梢岳斫鉃檫\行期間,對象中方法的動態(tài)攔截,在攔截方法的前后執(zhí)行功能操作。

代理類在程序運行期間,創(chuàng)建的代理對象稱之為動態(tài)代理對象。這種情況下,創(chuàng)建的代理對象,并不是事先在Java代碼中定義好的。而是在運行期間,根據(jù)我們在動態(tài)代理對象中的“指示”,動態(tài)生成的。也就是說,你想獲取哪個對象的代理,動態(tài)代理就會為你動態(tài)的生成這個對象的代理對象。動態(tài)代理可以對被代理對象的方法進(jìn)行功能增強(qiáng)。有了動態(tài)代理的技術(shù),那么就可以在不修改方法源碼的情況下,增強(qiáng)被代理對象的方法的功能,在方法執(zhí)行前后做任何你想做的事情。

創(chuàng)建代理對象的兩個方法:

//JDK動態(tài)代理

Proxy.newProxyInstance(三個參數(shù));

//CGLib動態(tài)代理

Enhancer.create(兩個參數(shù));

正常類創(chuàng)建對象的過程:

動態(tài)代理


動態(tài)代理創(chuàng)建代理對象的過程:

動態(tài)代理3


1.2 兩種常用的動態(tài)代理方式

1、基于接口的動態(tài)代理

·提供者:JDK

·使用JDK官方的Proxy類創(chuàng)建代理對象

·注意:代理的目標(biāo)對象必須實現(xiàn)接口

2、基于類的動態(tài)代理

·提供者:第三方 CGLib

·使用CGLib的Enhancer類創(chuàng)建代理對象

·注意:如果報 asmxxxx 異常,需要導(dǎo)入 asm.jar包

public class LogProxy {
    /**
     * 生成對象的代理對象,對被代理對象進(jìn)行所有方法日志增強(qiáng)
     * 參數(shù):原始對象
     * 返回值:被代理的對象
     * JDK 動態(tài)代理
     *  基于接口的動態(tài)代理
     *  被代理類必須實現(xiàn)接口
     *  JDK提供的
     */
    public static Object getObject(final Object obj){
        /**
         * 創(chuàng)建對象的代理對象
         * 參數(shù)一:類加載器
         * 參數(shù)二:對象的接口
         * 參數(shù)三:調(diào)用處理器,代理對象中的方法被調(diào)用,都會在執(zhí)行方法。對所有被代理對象的方法進(jìn)行攔截
         */
        Object proxyInstance = Proxy.newProxyInstance(obj.getClass().getClassLoader()
                , obj.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //方法執(zhí)行前
                long startTime = System.currentTimeMillis();

                Object result = method.invoke(obj, args);//執(zhí)行方法的調(diào)用

                //方法執(zhí)行后
                long endTime = System.currentTimeMillis();
                SimpleDateFormat sdf = new SimpleDateFormat();
                System.out.printf(String.format("%s方法執(zhí)行結(jié)束時間:%%s ;方法執(zhí)行耗時:%%d%%n"
                        , method.getName()), sdf.format(endTime), endTime - startTime);
                return result;
            }
        });
        return proxyInstance;
    }
    /**
     * 使用CGLib創(chuàng)建動態(tài)代理對象
     * 第三方提供的的創(chuàng)建代理對象的方式CGLib
     * 被代理對象不能用final修飾
     * 使用的是Enhancer類創(chuàng)建代理對象
     */
    public static Object getObjectByCGLib(final Object obj){
        /**
         * 使用CGLib的Enhancer創(chuàng)建代理對象
         * 參數(shù)一:對象的字節(jié)碼文件
         * 參數(shù)二:方法的攔截器
         */
        Object proxyObj = Enhancer.create(obj.getClass(), new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //方法執(zhí)行前
                long startTime = System.currentTimeMillis();

                Object invokeObject = method.invoke(obj, objects);//執(zhí)行方法的調(diào)用

                //方法執(zhí)行后
                long endTime = System.currentTimeMillis();
                SimpleDateFormat sdf = new SimpleDateFormat();
                System.out.printf(String.format("%s方法執(zhí)行結(jié)束時間:%%s ;方法執(zhí)行耗時:%%d%%n"
                        , method.getName()), sdf.format(endTime), endTime - startTime);
                return invokeObject;
            }
        });
        return proxyObj;
    }
}


猜你喜歡:
一文詳解Proxy動態(tài)代理的內(nèi)部機(jī)制

分享到:
在線咨詢 我要報名
和我們在線交談!