JDK 動態代理與 CGLIB 動態代理,它倆真的不一樣
摘要: 一文帶你搞懂JDK 動態代理與 CGLIB 動態代理
本文分享自華為雲社區《 一文帶你搞懂JDK 動態代理與 CGLIB 動態代理 》,作者: Code皮皮蝦 。
兩者有何區別
1、Jdk動態代理:利用攔截器(必須實現InvocationHandler接口)加上 反射機制 生成一個代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理
2、 Cglib動態代理:利用ASM框架,對代理對象類生成的class文件加載進來,通過 修改其字節碼生成子類來進行代理
所以:
- 如果想要實現JDK動態代理那麼代理類必須實現接口,否則不能使用;
- 如果想要使用CGlib動態代理,那麼代理類不能使用final修飾類和方法;
還有: 在jdk6、jdk7、jdk8逐步對JDK動態代理優化之後,在調用次數較少的情況下,JDK代理效率高於CGLIB代理效率,只有當進行大量調用的時候,jdk6和jdk7比CGLIB代理效率低一點,但是到jdk8的時候,jdk代理效率高於CGLIB代理。
如何實現
JDK動態代理
UserService接口
public interface UserService { void addUser(); void updateUser(String str); }
UserServiceImpl實現類
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("添加用户"); } @Override public void updateUser(String str) { System.out.println("更新用户信息" + str); } }
UserProxy代理類,實現InvocationHandler接口重寫invoke方法
public class UserProxy implements InvocationHandler { private Object target; public UserProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object res = method.invoke(target, args); System.out.println("記錄日誌"); return res; } }
test測試類
public class test { public static void main(String[] args) { UserServiceImpl impl = new UserServiceImpl(); UserProxy userProxy = new UserProxy(impl); UserService userService = (UserService) Proxy.newProxyInstance(impl.getClass().getClassLoader(),impl.getClass().getInterfaces(),userProxy); userService.addUser(); userService.updateUser(":我是皮皮蝦"); } }
可見實現了增強,打印出記錄日誌
CGlib動態代理
CGlib不像是JDK動態代理,CGlib需要導入Jar包,那麼我用SpringBoot直接導入依賴
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
UserServiceImpl被代理類
public class UserServiceImpl { public void addUser() { System.out.println("添加了一個用户"); } public void deleteUser() { System.out.println("刪除了一個用户"); } }
UserServiceCGlib代理
public class UserServiceCGlib implements MethodInterceptor { private Object target; public UserServiceCGlib() { } public UserServiceCGlib(Object target) { this.target = target; } //返回一個代理對象: 是 target對象的代理對象 public Object getProxyInstance() { //1. 創建一個工具類 Enhancer enhancer = new Enhancer(); //2. 設置父類 enhancer.setSuperclass(target.getClass()); //3. 設置回調函數 enhancer.setCallback(this); //4. 創建子類對象,即代理對象 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("增強開始~~~"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("增強結束~~~"); return result; } }
test測試類
public class test { public static void main(String[] args) { UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl()); UserServiceImpl userService = (UserServiceImpl)serviceCGlib.getProxyInstance(); userService.addUser(); System.out.println(); userService.deleteUser(); } }
可見實現了增強,打印出記錄日誌
使用場景
到這裏相信各位小夥伴們已經基本掌握了JDK動態代理和CGlib動態代理的區別和實現
但是,如果是在面試過程中,除了要答出以上要點,你還要回答出它們的使用場景,這其實就是面試的加分項
那麼,這兩個動態代理的使用場景是什麼呢???
答案:Spring AOP
以下是Spring AOP創建代理的方法
@Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } //如果 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
1、如果目標對象實現了接口, 默認情況下會採用JDK的動態代理
2、如果目標對象實現了接口,也可以 強制使用CGLIB
3、如果目標對象 沒有實現了接口,必須採用CGLIB庫 ,spring會自動在JDK動態代理和CGLIB之間轉換
如果需要強制使用CGLIB來實現AOP,需要配置spring.aop.proxy-target-class=true或@EnableAspectJAutoProxy(proxyTargetClass = true
「其他文章」
- 記一次批量更新整型類型的列 → 探究 UPDATE 的使用細節
- 編碼中的Adapter,不僅是一種設計模式,更是一種架構理念與解決方案
- 線程池底層原理詳解與源碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裏都有
- Django 之路由層
- 【前端必會】webpack loader 到底是什麼
- day42-反射01
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8源碼解讀
- 詳解JS中 call 方法的實現
- 打印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 設計模式---享元模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang代碼
- [ML從入門到入門] 支持向量機:從SVM的推導過程到SMO的收斂性討論
- 從應用訪問Pod元數據-DownwardApi的應用
- Springboot之 Mybatis 多數據源實現
- Java 泛型程序設計
- CAS核心思想、底層實現