動態代理的兩種實現方式

JDK動態代理java

利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。

CGlib動態代理數組

利用ASM(開源的Java字節碼編輯庫,操做字節碼)開源包,將代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。

區別框架

JDK代理只能對實現接口的類生成代理;CGlib是針對類實現代理,對指定的類生成一個子類,並覆蓋其中的方法,這種經過繼承類的實現方式,不能代理final修飾的類。

方式一:JDK動態代理

目標接口類ide

/**
 * 目標接口類
 */
public interface UserManager {
    void addUser(String username, String password);
    void delUser(String username);
}

接口實現類測試

/**
 * 動態代理:
 *      1. 特色:字節碼隨用隨建立,隨用隨加載
 *      2. 做用:不修改源碼的基礎上對方法加強
 *      3. 分類:
 *              1)基於接口的動態代理
 *                      1. 基於接口的動態代理:
 *                              1)涉及的類:Proxy
 *                              2)提供者:JDK官方
 *                              3)如何建立代理對象:
 *                                      使用Proxy類中的newProxyInstance方法
 *                              4)建立代理對象的要求
 *                                      被代理類最少實現一個接口,若是沒有則不能使用
 *                              5)newProxyInstance方法的參數:
 *                                      ClassLoader:類加載器,它是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。固定寫法。
 *                                      Class[]:字節碼數組,它是用於讓代理對象和被代理對象有相同方法。固定寫法。
 *                                      InvocationHandler:用於提供加強的代碼,它是讓咱們寫如何代理。咱們通常都是些一個該接口的實現類,一般狀況下都是匿名內部類
 *              2)基於子類的動態代理
 */
public class JDKProxy implements InvocationHandler {
    // 用於指向被代理對象
    private Object targetObject;
    public Object newProxy(Object targetObject) {
        // 將被代理對象傳入進行代理
        this.targetObject = targetObject;
        // 返回代理對象
        return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);
    }

    /**
     * 被代理對象的任何方法執行時,都會被invoke方法替換,即:代理對象執行被代理對象中的任何方法時,實際上執行的時當前的invoke方法
     * @param proxy(代理對象的引用)
     * @param method(當前執行的方法)
     * @param args(當前執行方法所需的參數)
     * @return(和被代理對象方法有相同的返回值)
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在原來的方法上增長了日誌打印功能,加強代碼
        printLog();
        Object ret = null;
        // 調用invoke方法(即執行了代理對象調用被調用對象中的某個方法)
        ret = method.invoke(targetObject, args);
        return ret;
    }

    /**
     * 模擬日誌打印
     */
    private void printLog() {
        System.out.println("日誌打印:printLog()");
    }
}

測試類this

public class TestJDKProxy {
    public static void main(String[] args) {
        UserManager userManager = new UserManagerImpl();
        JDKProxy jdkProxy = new JDKProxy();
        UserManager userManagerProxy = (UserManager)jdkProxy.newProxy(userManager);
        System.out.println("--------------------沒有使用加強過的方法--------------------");
        userManager.addUser("root","root");
        userManager.delUser("root");
        System.out.println("--------------------使用代理對象加強過的方法--------------------");
        userManagerProxy.addUser("scott","tiger");
        userManagerProxy.delUser("scott");
    }
}

測試結果.net

--------------------沒有使用加強過的方法--------------------
調用了UserManagerImpl.addUser()方法!
調用了UserManagerImpl.delUser()方法!
--------------------使用代理對象加強過的方法--------------------
日誌打印:printLog()
調用了UserManagerImpl.addUser()方法!
日誌打印:printLog()
調用了UserManagerImpl.delUser()方法!

方式二:CGlib動態代理

/**
 * 動態代理:
 *      1. 特色:字節碼隨用隨建立,隨用隨加載
 *      2. 做用:不修改源碼的基礎上對方法加強
 *      3. 分類:
 *              1)基於接口的動態代理
 *              2)基於子類的動態代理
 *                      1. 基於子類的動態代理:
 *                              1)涉及的類:Enhancer
 *                              2)提供者:第三方cglib庫
 *                              3)如何建立代理對象:
 *                                      使用Enhancer類中的create方法
 *                              4)建立代理對象的要求
 *                                      被代理類不能是最終類
 *                              5)create方法的參數:
 *                                      Class:字節碼,它是用於指定被代理對象的字節碼。固定寫法。
 *                                      Callback():用於提供加強的代碼,它是讓咱們寫如何代理。咱們通常都是些一個該接口的實現類。固定寫法。
 */
public class CGLibProxy implements MethodInterceptor {
    // 用於指向被代理對象
    private Object targetObject;

    // 用於建立代理對象
    public Object createProxy(Object targetObject) {
        this.targetObject = targetObject;
        return new Enhancer().create(this.targetObject.getClass(),this);
    }

    /**
     * 
     * @param proxy(代理對象的引用)
     * @param method(當前執行的方法)
     * @param args(當前執行方法所需的參數)
     * @param methodProxy(當前執行方法的代理對象)
     * @return(和被代理對象方法有相同的返回值)
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object ret = null;
        // 過濾方法
        if ("addUser".equals(method.getName())) {
            // 日誌打印
            printLog();
        }
        ret = method.invoke(targetObject, args);
        return ret;
    }

    /**
     * 模擬日誌打印
     */
    private void printLog() {
        System.out.println("日誌打印:printLog()");
    }
}

測試類代理

public class TestCGLibProxy {
    public static void main(String[] args) {
        CGLibProxy cgLibProxy = new CGLibProxy();
        UserManager userManager = new UserManagerImpl();
        UserManager cgLibProxyProxy = (UserManager)cgLibProxy.createProxy(userManager);
        System.out.println("--------------------沒有使用加強過的方法--------------------");
        userManager.addUser("root","root");
        userManager.delUser("root");
        System.out.println("--------------------使用代理對象加強過的方法--------------------");
        cgLibProxyProxy.addUser("scott","tiger");
        cgLibProxyProxy.delUser("scott");
    }
}

測試結果日誌

--------------------沒有使用加強過的方法--------------------
調用了UserManagerImpl.addUser()方法!
調用了UserManagerImpl.delUser()方法!
--------------------使用代理對象加強過的方法--------------------
日誌打印:printLog()
調用了UserManagerImpl.addUser()方法!
調用了UserManagerImpl.delUser()方法!

總結

1)JDK代理使用的是反射機制實現aop的動態代理,CGLIB代理使用字節碼處理框架asm,經過修改字節碼生成子類。因此jdk動態代理的方式建立代理對象效率較高,執行效率較低,cglib建立效率較低,執行效率高;
2)JDK動態代理機制是委託機制,具體說動態實現接口類,在動態生成的實現類裏面委託hanlder去調用原始實現類方法,CGLIB則使用的繼承機制,具體說被代理類和代理類是繼承關係,因此代理類是能夠賦值給被代理類的,若是被代理類有接口,那麼代理類也能夠賦值給接口。code

原文連接:https://blog.csdn.net/weixin_...

相關文章
相關標籤/搜索