2、Spring的兩種代理JDK和CGLIB的區別淺談

一、原理區別

Java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

1、如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP
2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP
3、如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

如何強制使用CGLIB實現AOP?
(1)添加CGLIB庫,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入

JDK動態代理和CGLIB字節碼生成的區別?
(1)JDK動態代理只能對實現了接口的類生成代理,而不能針對類
(2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
因爲是繼承,所以該類或方法最好不要聲明成final

二、代碼實現

package com.fy.spring.proxy;    

public interface UserManager {    
    public void addUser(String id, String password);    
    public void delUser(String id);    
}   

package com.fy.spring.proxy;    
public class UserManagerImpl implements UserManager {    
    public void addUser(String id, String password) {    
        System.out.println(".: 掉用了UserManagerImpl.addUser()方法! ");    

    }    

    public void delUser(String id) {    
        System.out.println(".: 掉用了UserManagerImpl.delUser()方法! ");    

    }    
}   

JDK動態代理類

package com.fy.spring.proxy;    
import java.lang.reflect.InvocationHandler;    
import java.lang.reflect.Method;    
import java.lang.reflect.Proxy;    
/**    
 * JDK動態代理類   
 */    
public class JDKProxy implements InvocationHandler {    

    private Object targetObject;//需要代理的目標對象    

    public Object newProxy(Object targetObject) {//將目標對象傳入進行代理    
        this.targetObject = targetObject;     
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),    
                targetObject.getClass().getInterfaces(), this);//返回代理對象    
    }    

    public Object invoke(Object proxy, Method method, Object[] args)//invoke方法    
            throws Throwable {    
        checkPopedom();    //一般我們進行邏輯處理的函數比如這個地方是模擬檢查權限    
        return method.invoke(targetObject, args);  //調用invoke方法,ret存儲該方法的返回值    
    }    

    private void checkPopedom() {//模擬檢查權限的例子    
        System.out.println(".:檢查權限  checkPopedom()!");    
    }    
} 

CGLibProxy動態代理類

package com.fy.spring.proxy;    

import java.lang.reflect.Method;    

import net.sf.cglib.proxy.Enhancer;    
import net.sf.cglib.proxy.MethodInterceptor;    
import net.sf.cglib.proxy.MethodProxy;    

/**   
 * CGLibProxy動態代理類的實例   
 *     
 *    
 */    
public class CGLibProxy implements MethodInterceptor {    

    private Object targetObject;// CGLib需要代理的目標對象    

    public Object createProxyObject(Object obj) {    
        this.targetObject = obj;    
        Enhancer enhancer = new Enhancer();    
        enhancer.setSuperclass(obj.getClass());    
        enhancer.setCallback(this);    
        Object proxyObj = enhancer.create();    
        return proxyObj;// 返回代理對象    
    }    

    public Object intercept(Object proxy, Method method, Object[] args,    
            MethodProxy methodProxy) throws Throwable {    
        Object obj = null;    
        if ("addUser".equals(method.getName())) {// 過濾方法    
            checkPopedom();// 檢查權限    
        }    
        obj = method.invoke(targetObject, args);    
        return obj;    
    }    

    private void checkPopedom() {    
        System.out.println(".:檢查權限  checkPopedom()!");    
    }    
}   

測試類:

public class Client {    
    public static void main(String[] args) {    
        UserManager userManager = (UserManager) new CGLibProxy()    
                .createProxyObject(new UserManagerImpl());    
        System.out.println("-----------CGLibProxy-------------");    
        userManager.addUser("tom", "root");    
        System.out.println("-----------JDKProxy-------------");    
        JDKProxy jdkPrpxy = new JDKProxy();    
        UserManager userManagerJDK = (UserManager) jdkPrpxy    
                .newProxy(new UserManagerImpl());    
        userManagerJDK.addUser("tom", "root");    
    }    

}   

運行結果:
———–CGLibProxy————-
檢查權限 checkPopedom()!

———–JDKProxy————-
檢查權限 checkPopedom()!

三、區別

JDK代理是不需要依賴第三方的庫,只要JDK環境就可以進行代理,它有幾個要求
* 實現InvocationHandler
* 使用Proxy.newProxyInstance產生代理對象
* 被代理的對象必須要實現接口
使用JDK動態代理,目標類必須實現的某個接口,如果某個類沒有實現接口則不能生成代理對象。

CGLib 必須依賴於CGLib的類庫,Cglib原理是針對目標類生成一個子類,覆蓋其中的所有方法,所以目標類和方法不能聲明爲final類型。針對接口編程的環境下推薦使用JDK的代理。從執行效率上看,Cglib動態代理效率較高。在Hibernate中的攔截器其實現考慮到不需要其他接口的條件Hibernate中的相關代理採用的是CGLib來執行。

發佈了74 篇原創文章 · 獲贊 32 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章