靜態代理 一個代理類 只能代理一個類
實現介面,傳入被代理類,方法攔截
動態代理
jdk動態代理(反射虛擬生成代理物件) ,需要介面
jdk動態代理,必須是面向介面,目標業務類必須實現介面
什麼是動態代理
1.代理物件,不需要實現介面
2.代理物件的生成,是利用JDK的API,動態的在記憶體中構建代理物件(需要我們指定建立代理物件/目標物件實現的介面的型別)
3.動態代理也叫做:JDK代理,介面代理
JDK動態代理
1)原理:是根據類載入器和介面建立代理類(此代理類是介面的實現類,所以必須使用介面 面向介面生成代理,位於java.lang.reflect包下)
2)實現方式:
1. 通過實現InvocationHandler介面建立自己的呼叫處理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2. 通過為Proxy類指定ClassLoader物件和一組interface建立動態代理類Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3. 通過反射機制獲取動態代理類的建構函式,其引數型別是呼叫處理器介面型別Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4. 通過建構函式建立代理類例項,此時需將呼叫處理器物件作為引數被傳入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
// 每次生成動態代理類物件時,實現了InvocationHandler介面的呼叫處理器物件 public class InvocationHandlerImpl implements InvocationHandler { private Object target;// 這其實業務實現類物件,用來呼叫具體的業務方法 // 通過建構函式傳入目標物件 public InvocationHandlerImpl(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("呼叫開始處理"); result = method.invoke(target, args); System.out.println("呼叫結束處理"); return result; } public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 被代理物件 IUserDao userDao = new UserDao(); InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao); ClassLoader loader = userDao.getClass().getClassLoader(); Class<?>[] interfaces = userDao.getClass().getInterfaces(); // 主要裝載器、一組介面及呼叫處理動態代理例項 IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl); newProxyInstance.save(); } }
cglib(基於asm位元組碼技術虛擬生成代理類) 只需要字類實現
CGLIB動態代理
原理:利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。
什麼是CGLIB動態代理
使用cglib[Code Generation Library]實現動態代理,並不要求委託類必須實現介面,底層採用asm位元組碼生成框架生成代理類的位元組碼
CGLIB動態代理相關程式碼
public class CglibProxy implements MethodInterceptor { private Object targetObject; // 這裡的目標型別為Object,則可以接受任意一種引數作為被代理類,實現了動態代理 public Object getInstance(Object target) { // 設定需要建立子類的類 this.targetObject = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開啟事物"); Object result = proxy.invoke(targetObject, args); System.out.println("關閉事物"); // 返回代理物件 return result; } public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao()); userDao.save(); } }
CGLIB動態代理與JDK動態區別
java動態代理是利用反射機制生成一個實現代理介面的匿名類,在呼叫具體方法前呼叫InvokeHandler來處理。
而cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。
Spring中。
1、如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP
2、如果目標物件實現了介面,可以強制使用CGLIB實現AOP
3、如果目標物件沒有實現了介面,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
JDK動態代理只能對實現了介面的類生成代理,而不能針對類 。
CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。
因為是繼承,所以該類或方法最好不要宣告成final ,final可以阻止繼承和多型。