1. 程式人生 > >Spring的兩種代理JDK和CGLIB的區別淺談

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配置檔案中加入<aop:aspectj-autoproxy proxy-target-class="true"/>


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();//一般我們進行邏輯處理的函式比如這個地方是模擬檢查許可權  
        Object ret = null;      // 設定方法的返回值  
        ret  = method.invoke(targetObject, args);       //呼叫invoke方法,ret儲存該方法的返回值  
        return 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()!  
掉用了UserManagerImpl.addUser()方法!   
-----------JDKProxy-------------  
檢查許可權  checkPopedom()!  
掉用了UserManagerImpl.addUser()方法!

JDK代理是不需要以來第三方的庫,只要要JDK環境就可以進行代理,它有幾個要求
* 實現InvocationHandler 
* 使用Proxy.newProxyInstance產生代理物件
* 被代理的物件必須要實現介面
CGLib 必須依賴於CGLib的類庫,但是它需要類來實現任何介面代理的是指定的類生成一個子類,覆蓋其中的方法,是一種繼承但是針對介面程式設計的環境下推薦使用JDK的代理
在Hibernate中的攔截器其實現考慮到不需要其他介面的條件Hibernate中的相關代理採用的是CGLib來執行。