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
- 帶你掌握 C 中三種類成員初始化方式
- 實踐GoF的設計模式:工廠方法模式
- DCM:一個能夠改善所有應用資料互動場景的中介軟體新秀
- 手繪圖解java類載入原理
- 關於加密通道規範,你真正用的是TLS,而非SSL
- 程式碼重構,真的只有複雜化一條路嗎?
- 解讀分散式排程平臺Airflow在華為雲MRS中的實踐
- 透過例項demo帶你認識gRPC
- 帶你聚焦GaussDB(DWS)儲存時遊標使用
- 傳統到敏捷的轉型中,誰更適合做Scrum Master?
- 輕鬆解決研發知識管理難題
- Java中觀察者模式與委託,還在傻傻分不清
- 如何使用Python實現影象融合及加法運算?
- 什麼是強化學習?
- 探索開源工作流引擎Azkaban在MRS中的實踐
- GaussDB(DWS) NOT IN優化技術解密:排他分析場景400倍效能提升
- Java中觀察者模式與委託,還在傻傻分不清
- Java中的執行緒到底有哪些安全策略
- 一圖詳解java-class類檔案原理
- Java中的執行緒到底有哪些安全策略