靜態代理,動態代理和CGLIB代理模式
代理模式
一、概述
代理是一種模式,提供了對目標物件的間接訪問方式,即通過代理訪問目標物件。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求,同時代理模式便於擴充套件目標物件功能的特點也為多人所用。
二、圖形描述
三、程式碼實現
靜態代理,由於比較簡單,所有把程式碼都合到一起了:
// 需要被代理的介面 public interface BussinessInterface { void execute(); } // 基礎實現類 public class Bussiness implements BussinessInterface { @Override public void execute() { System.out.println("柯賢銘在做生意~~~"); } } // 在基礎實現類的基礎之上,封裝一層方法 public class BussinessProxy implements BussinessInterface { private BussinessInterface bussinessImpl; public BussinessProxy(BussinessInterface bussinessImpl) { this.bussinessImpl = bussinessImpl; } @Override public void execute() { System.out.println("前攔截..."); bussinessImpl.execute(); System.out.println("後攔截..."); } } // 測試類,真正使用的時候,我們採用增強之後的實現類 public class TestAgent { public static void main(String[] args) { BussinessInterface bussinessInterface = new Bussiness(); BussinessInterface newBuss = new BussinessProxy(bussinessInterface); newBuss.execute(); } }
效果截圖:
靜態總結:
優點:可以做到不對目標物件進行修改的前提下,對目標物件進行功能的擴充套件和攔截。
缺點:因為代理物件,需要實現與目標物件一樣的介面,會導致代理類十分繁多,不易維護,同時一旦介面增加方法,則目標物件和代理類都需要維護。
動態代理模式,由於程式碼量較少,我也融合到一起:
// 定義介面 public interface UserService { void saveUser(); } // 定義介面實現類及方法 public class UserServiceImpl implements UserService { @Override public void saveUser() { System.out.println("呼叫 saveUser() 方法"); } } // 定義代理工具類 public class MyProxyUtil { public static UserService getProxyByJDK(UserService service) { // 引數:目標物件的類載入器,目標物件的介面,代理物件的執行處理器 UserService userService = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("記錄日誌-開始"); Object obj = method.invoke(service, args); System.out.println("記錄日誌-結束"); return obj; } }); return userService; } } // 測試類 public class Test { public static void main(String[] args) { // 建立目標物件 UserService userService = new UserServiceImpl(); // 生成代理物件 UserService proxy = MyProxyUtil.getProxyByJDK(userService); // 呼叫目標物件方法 userService.saveUser(); System.out.println("==================================="); // 呼叫代理物件方法 proxy.saveUser(); } }
測試截圖:
動態代理的總結
優點:代理物件無需實現介面,免去了編寫很多代理類的煩惱,同時介面增加方法也無需再維護目標物件和代理物件,只需在事件處理器中新增對方法的判斷即可。
缺點:代理物件不需要實現介面,但是目標物件一定要實現介面,否則無法使用JDK動態代理。
CGLib 動態代理
CGLib 動態代理相對於 JDK 動態代理侷限性就小了很多,目標物件不需要實現介面,底層是通過繼承目標物件產生代理子物件
程式碼,只是工具類方法多了一個:
public static UserService getProxyByCglib(UserService service) { // 建立增強器 Enhancer enhancer = new Enhancer(); // 設定需要增強的類的物件 enhancer.setSuperclass(UserServiceImpl.class); // 設定回撥方法 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { long begin = System.currentTimeMillis(); System.out.println("記錄程式開始"); Object object = methodProxy.invokeSuper(o, args); long end = System.currentTimeMillis(); System.out.println("記錄程式結束"); return object; } }); UserService userService = (UserService) enhancer.create(); return userService; }
測試程式碼:
public class TestCglib {
public static void main(String[] args) {
// 建立目標物件
UserService userService = new UserServiceImpl();
// 生成代理物件
UserService proxy = MyProxyUtil.getProxyByCglib(userService);
// 呼叫目標物件方法
userService.saveUser();
System.out.println("===================================");
// 呼叫代理物件方法
proxy.saveUser();
}
}
測試結果:
總結:
Cglib代理: 針對類來實現代理,對指定目標 產生一個子類 通過方法攔截技術攔截所有父類方法的呼叫。 我們要使用cglib代理必須引入 cglib的jar包
三種代理模式進行總的分析概括:
代理模式:代理類和被代理類實現共同的介面(或繼承),代理類中存有指向被代理類的索引,實際執行時通過呼叫代理類的方法、實際執行的是被代理類的方法。
而AOP,是通過動態代理實現的。
一、簡單來說:
JDK動態代理只能對實現了介面的類生成代理,而不能針對類
CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法(繼承)
二、Spring在選擇用JDK還是CGLiB的依據:
(1)當Bean實現介面時,Spring就會用JDK的動態代理
(2)當Bean沒有實現介面時,Spring使用CGlib是實現
(3)可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
三、CGlib比JDK快?
(1)使用CGLib實現動態代理,CGLib底層採用ASM位元組碼生成框架,使用位元組碼技術生成代理類,比使用Java反射效率要高。唯一需要注意的是,CGLib不能對宣告為final的方法進行代理,因為CGLib原理是動態生成被代理類的子類。
(2)在對JDK動態代理與CGlib動態代理的程式碼實驗中看,1W次執行下,JDK7及8的動態代理效能比CGlib要好20%左右。