網站首頁 編程語言 正文
一、代理模式
代理模式是為某一個對象(委托類)提供一個代理(代理類),用來控制對這個對象的訪問。委托類和代理類有一個共同的父類或父接口,代理類會對請求做預處理、過濾,將請求分配給指定的對象。
生活中的常見代理有:租房中介、婚慶公司等。
代理類的兩個設計原則:
- 代理類與委托類具有相似的行為
- 代理類增強委托類的行為
常用的代理模式:
- 靜態代理
- 動態代理
二、靜態代理
1.代理三要素 (以結婚為例)
- 有共同的行為(結婚)- 定義接口
- 目標角色/真實角色(新人)- 實現接口
- 代理角色(婚慶公司)- 實現接口 增強用戶行為
2.代碼
(1)有共同的行為(結婚)- 定義接口
public interface Marry {
void toMarry();
}
(2)目標角色/真實角色(新人)- 實現接口
public class You implements Marry{
@Override
public void toMarry() {
System.out.println("我結婚了");
}
}
(3)代理角色(婚慶公司)- 實現接口 增強用戶行為
public class MarryProxy implements Marry{
private Marry target;
public MarryProxy(Marry target) {
this.target = target;
}
@Override
public void toMarry() {
before();
target.toMarry();
after();
}
private void after() {
System.out.println("百年好合");
}
private void before() {
System.out.println("婚禮現場布置中");
}
}
(4)測試
public static void main(String[] args) {
You you = new You();
MarryProxy marryProxy = new MarryProxy(you);
marryProxy.toMarry();
}
結果如下:
3.靜態代理特點
- 目標角色固定
- 在應用程序之前就得知目標角色
- 代理對象會增強目標對象的行為
- 有可能存在多個代理,產生“類爆炸”
三、動態代理
可以根據需要,通過反射機制在程序運行期,動態的為目標對象創建代理對象。
動態代理的兩種實現方式:
- JDK動態代理
- CGLIB動態代理
動態代理的特點:
- 目標對象不固定
- 在程序運行時,動態創建目標對象
- 代理對象會增強目標對象的行為
四、JDK動態代理
1.實現過程
public class JdkHandler implements InvocationHandler {
private Object target;
public JdkHandler(Object target) {
this.target = target;
}
//得到代理對象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法執行前");
Object object = method.invoke(target, args);
System.out.println("方法執行后");
return object;
}
}
(1)定義一個動態代理類,該類去實現InvocationHandler
這個接口。
(2)在該類中定義一個方法,使用Proxy.newProxyInstance
得到代理對象,newProxyInstance
有三個參數,如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
-
ClassLoader loader
:該動態代理類的類加載器 -
Class<?>[] interfaces
:目標對象的接口數組 -
InvocationHandler h
:傳入InvocationHandler 接口的實現類,在這里填入的是該動態代理類,因為該動態代理類去實現了InvocationHandler 這個接口。
(3)重寫InvocationHandler
中的invoke
方法,調用目標對象的方法并且增強目標對 象的行為。invoke
方法同樣有三個參數,如下:
public Object invoke(Object proxy, Method method, Object[] args)
-
Object proxy
:調用該方法的代理實例 -
Method method
:目標對象的方法 -
Object[] args
:目標對象的方法所需要的參數
(4)測試:
public static void main(String[] args) {
You you = new You();
JdkHandler jdkHandler = new JdkHandler(you);
Marry proxy = (Marry)jdkHandler.getProxy();
proxy.toMarry();
}
結果:
以上結果說明,當執行到proxy.toMarry()這行代碼時,底層會自動去調用動態代理類JdkHandler
中的invoke
方法。
2.執行流程
五、CGLIB動態代理
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能使用JDK的動態代理。
cglib是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。
cglib動態的原理如下圖:
可以看到,每次調用代理類的方法都會被攔截器所攔截,在攔截器中才是調用目標類的該方法的邏輯。
1.引入依賴
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2.實現過程
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxy(){
Enhancer enhancer = new Enhancer();
//設置目標類成為父類
enhancer.setSuperclass(target.getClass());
//設置回調 回調對象為本身
enhancer.setCallback(this);
//生成代理類對象并返回
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法執行前");
Object object = methodProxy.invoke(target, objects);
System.out.println("方法執行后");
return object;
}
}
(1)定義一個類去實現MethodInterceptor
接口,并且重寫intercept
攔截器方法。
(2)在類中定義一個方法getProxy
,在方法中設置:讓目標類成為該動態代理類的父類,并且設置回調對象為本身。最后通過create
方法返回生成的代理對象。
(3)在intercept
攔截器方法調用目標類的方法,并且增強行為目標類的行為或寫上一些邏輯。
(4)測試:
public static void main(String[] args) {
You you = new You();
CglibProxy cglibProxy = new CglibProxy(you);
You proxy = (You) cglibProxy.getProxy();
proxy.toMarry();
}
結果:
以上結果說明,每次動態代理對象調用目標對象的方法時,都會被攔截器攔截,然后運行攔截器中的代碼。
六、JDK代理和CGLIB代理的區別
- JDK動態代理實現接口,Cglib動態代理繼承思想
- JDK動態代理(目標對象存在接口時)執行效率高于Cglib
- 如果目標對象有接口實現,選擇JDK代理,如果沒有接口實現選擇Cglib代理
原文鏈接:https://blog.csdn.net/rqt1013_/article/details/125623094
- 上一篇:springboot 視圖集成
- 下一篇:鼠標事件-事件對象
相關推薦
- 2022-12-26 C語言實現十六進制轉換為十進制的方法詳解_C 語言
- 2022-05-01 基于Python的Houdini插件開發過程詳情_python
- 2022-08-07 詳解Qt使用QImage類實現圖像基本操作_C 語言
- 2023-12-12 SSM整合 spring-mybaits配置文件——設置數據庫字段名駝峰命名規則
- 2022-03-26 正則表達式詳析+常用示例_正則表達式
- 2022-05-13 Headless Chrom自動化工具詳解
- 2022-11-30 關于pyinstaller?打包多個py文件的問題_python
- 2022-12-13 一文詳解Python加解壓文件gzip庫的操作_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支