【重溫設(shè)計(jì)模式】JDK動態(tài)代理和CGLIB動態(tài)代理

如果目標(biāo)類實(shí)現(xiàn)接口,采用JDK動態(tài)代理;如果目標(biāo)類沒有實(shí)現(xiàn)接口,采用CGLIB動態(tài)代理

一.JDK動態(tài)代理
//JDK動態(tài)代理 
public class SomeServiceTest { 
       @Test 
       public void someServiceTest(){ 
                
               SomeService target = new SomeServiceImpl(); 

              //loader:一個ClassLoader對象,定義了由哪個ClassLoader對象來生成代理對象進(jìn)行加載
              //interfaces:一個Interface對象的數(shù)組,表示的是我將要給我需要代理的對象提供一組什么接口,如果我提供了一組接口給它,那么這個代理對象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
             //h:一個InvocationHandler對象,表示的是當(dāng)我這個動態(tài)代理對象在調(diào)用方法的時候,會關(guān)聯(lián)到哪一個InvocationHandler對象上,間接通過invoke來執(zhí)行

               SomeService proxy = (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),//目標(biāo)類的類加載器 
                                                          target.getClass().getInterfaces(),//目標(biāo)類實(shí)現(xiàn)的所有接口 
                                                          new InvocationHandler() {//調(diào)用處理器 
                                                               /** 
                                                                * proxy:代理對象 
                                                                * method:目標(biāo)方法 
                                                                * args:目標(biāo)方法的參數(shù) 
                                                                */ 
                                                               @Override 
                                                               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
                                                                       SomeServiceUtil.doTransaction(); 
                                                                       //執(zhí)行目標(biāo)方法 (注意invoke中的第一個參數(shù)寫的是被代理對象)
                                                                       Object result = method.invoke(target, args); 
                                                                       SomeServiceUtil.doLog(); 
                                                                       return result; 
                                                               } 
                                                       }); 
              //通過代理對象調(diào)用方法
               proxy.doSome(); 
    
       } 

總結(jié)
1.JDK內(nèi)置的Proxy動態(tài)代理可以在運(yùn)行時動態(tài)生成字節(jié)碼,而沒必要針對每個類編寫代理類。中間主要使用到了一個接口InvocationHandler與Proxy.newProxyInstance靜態(tài)方法;
2.使用內(nèi)置的Proxy實(shí)現(xiàn)動態(tài)代理有一個問題:被代理的類必須實(shí)現(xiàn)接口,未實(shí)現(xiàn)接口則沒辦法完成動態(tài)代理。
3.如果項(xiàng)目中有些類沒有實(shí)現(xiàn)接口,則不應(yīng)該為了實(shí)現(xiàn)動態(tài)代理而刻意去抽出一些沒有實(shí)例意義的接口,通過cglib可以解決該問題。

二.CGLIB動態(tài)代理

CGLIB(Code Generation Library)是一個開源項(xiàng)目,是一個強(qiáng)大的,高性能,高質(zhì)量的Code生成類庫,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口,通俗說cglib可以在運(yùn)行時動態(tài)生成字節(jié)碼。

使用cglib完成動態(tài)代理,大概的原理是:cglib繼承被代理的類,重寫方法,織入通知,動態(tài)生成字節(jié)碼并運(yùn)行,因?yàn)槭抢^承所以final類是沒有辦法動態(tài)代理的

package com.zhangguo.Spring041.aop04;

import java.lang.reflect.Method;
import java.util.Random;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/*
 * 動態(tài)代理類
 * 實(shí)現(xiàn)了一個方法攔截器接口
 */
public class DynamicProxy implements MethodInterceptor {

    // 被代理對象
    Object targetObject;

    //Generate a new class if necessary and uses the specified callbacks (if any) to create a new object instance. 
    //Uses the no-arg constructor of the superclass.
    //動態(tài)生成一個新的類,使用父類的無參構(gòu)造方法創(chuàng)建一個指定了特定回調(diào)的代理實(shí)例
    public Object getProxyObject(Object object) {
        this.targetObject = object;
        //增強(qiáng)器,動態(tài)代碼生成器
        Enhancer enhancer=new Enhancer();
        //回調(diào)方法
        enhancer.setCallback(this);
        //設(shè)置生成類的父類類型
        enhancer.setSuperclass(targetObject.getClass());
        //動態(tài)生成字節(jié)碼并返回代理對象
        return enhancer.create();
    }

    // 攔截方法
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 被織入的橫切內(nèi)容,開始時間 before
        long start = System.currentTimeMillis();
        // 調(diào)用方法
        Object result = methodProxy.invoke(targetObject, args);
        // 被織入的橫切內(nèi)容,結(jié)束時間
        Long span = System.currentTimeMillis() - start;
        System.out.println("共用時:" + span);
      
        return result;
    }
}

總結(jié):
使用cglib可以實(shí)現(xiàn)動態(tài)代理,即使被代理的類沒有實(shí)現(xiàn)接口,但被代理的類必須不是final類。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容