Spring的兩種代理JDK和CGLIB的區別淺談
阿新 • • 發佈:2019-02-06
一、原理區別:
2、如果目標物件實現了介面,可以強制使用CGLIB實現AOP
JDK代理是不需要以來第三方的庫,只要要JDK環境就可以進行代理,它有幾個要求
* 實現InvocationHandler
* 使用Proxy.newProxyInstance產生代理物件
* 被代理的物件必須要實現介面
CGLib 必須依賴於CGLib的類庫,但是它需要類來實現任何介面代理的是指定的類生成一個子類,覆蓋其中的方法,是一種繼承但是針對介面程式設計的環境下推薦使用JDK的代理
在Hibernate中的攔截器其實現考慮到不需要其他介面的條件Hibernate中的相關代理採用的是CGLib來執行。
java動態代理是利用反射機制生成一個實現代理介面的匿名類,在呼叫具體方法前呼叫InvokeHandler來處理。
而cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。
1、如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP2、如果目標物件實現了介面,可以強制使用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);
}
JDK動態代理類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()方法! "); } }
CGLibProxy動態代理類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()!"); } }
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來執行。