Aop切面編程(2)--代理模式

1、代理模式的理解:不修改A對象的代碼的基礎上,對A代碼塊進行拓展。通過創建ProxyA代理對象,拓展A對象并調用A對象的核心功能;

即:不修改對象的源碼基礎上,創建代理對象,進行功能的附加和增強;

2、代理的分類:1)靜態代理;2)動態代理(jdk,cglib);

3、靜態代理:在編譯的過程中就已經將代理對象、被代理對象、接口確定下來了,class文件已經生成了;

實現步驟:
1、定義一個接口及其實現類;
2、創建一個代理類同樣實現這個接口
3、將目標對象注注入進代理類,然后在代理類的對應方法調用目標類中的對應方法。

3.1)定義一個接口及方法ParentPrint:

package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}

3.2)定義一個被代理的類,并實現接口ParentPrint,其中print方法就是我們需要代理的核心方法;

package com.example.demo.proxy;public class Printer implements  ParentPrint {@Overridepublic void print(String content) {System.out.println(content);}
}

3.3)定義代理類,構造一個有參數的構造器,并重寫print方法(其中通過傳入的參數調用被代理對象的print方法)。

package com.example.demo.proxy;/*
1、首先需要代理類ProxyStaticPrinter和被代理類Printer同時實現一個共同的接口 ;
2、代理類ProxyStaticPrinter需要在構造方法中注入接口定義對象,并重寫代理的方法print(前置后置處理),在其中加入注入對象的print方法;
3、實例化代理對象ProxyStaticPrinter,實例化被代理對象Printer;
4、將被代理對象通過參數注入到代理對象(ProxyStaticPrinter)有參構造器中;
5、代理對象調用處理過的方法;
*/
public class ProxyStaticPrinter implements ParentPrint {private ParentPrint parentPrint;public ProxyStaticPrinter(ParentPrint parentPrint) {this.parentPrint = parentPrint;}@Overridepublic void print(String content) {System.out.println("前置操作");parentPrint.print(content);System.out.println("后置操作");}}

3.4)測試,定義一個代理對象,將代理對象通過參數傳入被代理的對象中;

    //測試public static void main(String[] args) {ParentPrint parentPrint = new Printer();ParentPrint ProxyParentPrint = new ProxyStaticPrinter(parentPrint);ProxyParentPrint.print("測試");}

運行

3.5)總結:

目標對象注注入進代理類,然后在代理類的對應方法調用目標類中的對應方法。這樣的話,我們就可以通過代理類屏蔽對目標對象的訪問;

優點:被代理類無需實現接口
缺點:只能代理這個類,要想代理其他類,要想代理其他類需要寫新的代理方法。

4、動態代理:動態代理包含了jdk動態代理和cglib動態代理;

4.1)jkd動態代理:

實現步驟:
1、定義一個接口及其實現類;
2、自定義 InvocationHandler 并重寫invoke方法,在 invoke 方法中我們會調用原生方法(被代理類的方法)并自定義一些處理邏輯;
3、通過 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法創建代理對象;

4.1.1)定義一個接口

package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}

4.1.2)定義一個接口及方法ParentPrint:

package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}

4.1.3)定義一個被代理的類,并實現接口ParentPrint,其中print方法就是我們需要代理的核心方法;

package com.example.demo.proxy;public class Printer implements  ParentPrint {@Overridepublic void print(String content) {System.out.println(content);}
}

4.1.4)定義一個代理類ProxyJdkPrinter并實現InvocationHandler,實現invoke方法,最終調用method.invoke(parentPrint,args);

注意該Proxy類中是靜態方法,且接收的三個參數依次為:

ClassLoader loader:指定當前目標對象使用類加載器,獲取加載器的方法是固定的。
Class<?>[] interfaces:目標對象實現的接口的類型,使用泛型方式確認類型。
InvocationHandler:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法作為參數傳入。

package com.example.demo.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*jdk動態代理:前提目標類必須有父接口(接口:ParentPrint,目標類:Printer)
1、創建ProxyJdkPrinter類,繼承接口InvocationHandler創建代理類的調用處理程序;
2、同樣引入代理類的參數對象private ParentPrint parentPrint;
3、實現invoke方法,最終調用method.invoke(parentPrint,args);
4、測試:
實例化代理類p1;
將p1放入代理類ProxyJdkPrinter;
通過Proxy類的WuDaInvocationHandler方法創建代理對象;
代理對象調用被代理的方法;
* */
public class ProxyJdkPrinter implements InvocationHandler {private ParentPrint parentPrint;public ProxyJdkPrinter(ParentPrint parentPrint) {this.parentPrint = parentPrint;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();if(name.equals("print")){System.out.println("jdk增加操作");}return method.invoke(parentPrint,args);}}

4.1.5)測試,invoke() 方法: 當我們的動態代理對象調用原生方法的時候,最終實際上調用到的是 invoke() 方法,然后 invoke() 方法代替我們去調用了被代理對象的原生方法。

    public static void main(String[] args) {ParentPrint p1 = new Printer();ProxyJdkPrinter proxyJdkPrinter = new ProxyJdkPrinter(p1);//newProxyInstance:創建代理實例對象//三個參數://1、 目標類的類加載器 2、目標類的父接口  3、handlerParentPrint proxyPrint = (ParentPrint)Proxy.newProxyInstance(p1.getClass().getClassLoader(), p1.getClass().getInterfaces(), proxyJdkPrinter);proxyPrint.print("測試");}

運行

4.1.6)總結:

通過java提供的Proxy類幫我們創建代理對象,基于接口的動態代理需要利用JDK中的API,在JVM內存中動態的構建Proxy對象;
優點:可以生成所有實現接口的代理對象
缺點:JDK反射生成代理必須面向接口, 這是由Proxy的內部實現決定的。生成代理的方法中你必須指定實現類的接口,它根據這個接口來實現代理類生成的所實現的接口。

4.2)cglib動態代理:當目標沒有實現類的時候,可以使用;

實現步驟:

1、定義一個類;
2、自定義 MethodInterceptor 并重寫 intercept 方法,intercept 用于攔截增強被代理類的方法,和 JDK 動態代理中的 invoke 方法類似;
3、通過 Enhancer 類的 create()創建代理類;

4.2.1)定義一個被代理類Printer

package com.example.demo.proxy;public class Printer1 {public void print(String content) {System.out.println(content);}
}

4.2.2)定義一個代理類ProxyCglibPrinter,并實現MethodInterceptor

package com.example.demo.proxy;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*cglib動態代理:(目標對象不需要實現接口)
實現MethodInterceptor 接口,在調用目標對象的方法時,就可以實現在調用方法之前、調用方法過程中、調用方法之后對其進行控制。
1.創建目標對象target;
2.創建interceptor對象
3.創建Enhancer對象,它以目標類和interceptor作為原料,生產出代理對象
4、enhancer設置參數setSuperclass,setCallback
5、創建代理類proxy,調用需要代理的方法;
* */
public class ProxyCglibPrinter implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {String name = method.getName();if(name.equals("print")){System.out.println("cglib增加操作");}methodProxy.invokeSuper(o,objects);return null;}}

4.2.3)測試

    public static void main(String[] args) {Printer1 target = new Printer1();ProxyCglibPrinter interceptor = new ProxyCglibPrinter();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(interceptor);Printer1 proxy = (Printer1)enhancer.create();proxy.print("測試");}

運行

4.2.4)總結:

無需代理類實現接口,使用Cblib中的Enhancer來生成代理對象子類,并實現MethodInterceptor中的intercept方法,在此方法中可以實現增強功能。

如果目標對象需要實現接口,則使用JDK代理。
如果目標對象不需要實現接口,則使用Cglib代理。

5、綜上所述,再次總結:

靜態代理:

jdk動態代理:

cglib動態代理:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44466.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44466.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44466.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

端到端擁塞控制的本質

昨天整理了一篇 bbr 的微分方程組建模(參見 bbr 建模)&#xff0c;算是 bbr 算法終極意義上的一個總結&#xff0c;最后也順帶了對 aimd 的描述&#xff0c;算是我最近比較滿意的一篇分享了。那么接下來的問題&#xff0c;脫離出具體算法&#xff0c;上升到宏觀層面&#xff0c…

git reset hard和soft的使用和區別

在Git中&#xff0c;git reset命令用于撤銷提交、回溯版本和調整工作目錄或暫存區狀態&#xff0c;而不是gitrestore。git reset主要有三種模式&#xff1a;--soft、--mixed&#xff08;默認&#xff09;和--hard。以下是關于--hard和--soft兩種模式的使用方法和區別的詳細解釋…

uniapp微信小程序 TypeError: $refs[ref].push is not a function

我的寫法 this.$refs.addPopup.open();報錯 打印出來是這樣的 解決 參考未整理 原因 在當前頁面使用的v-for循環 并且循環體內也有組件使用了ref&#xff08;而我沒有把每個ref做區別命名&#xff09; 這樣就導致了我有很多同名的ref&#xff0c;然后就報錯了 解決辦法&a…

AI人工智能作詞,為音樂注入未來之力

在當今的音樂世界中&#xff0c;創新的力量不斷推動著邊界的拓展&#xff0c;而人工智能作詞正以其獨特的魅力&#xff0c;成為引領音樂走向未來的強大動力。 “妙筆生詞智能寫歌詞軟件&#xff08;veve522&#xff09;”無疑是這股浪潮中的璀璨明星。它利用先進的人工智能技術…

記錄一次Android推流、錄像踩坑過程

背景&#xff1a; 按照需求&#xff0c;需要支持APP在手機息屏時進行推流、錄像。 技術要點&#xff1a; 1、手機在息屏時能夠打開camera獲取預覽數據 2、獲取預覽數據時進行編碼以及合成視頻 一、息屏時獲取camera預覽數據&#xff1a; ①Camera.setPreviewDisplay(SurfaceH…

通過 Azure OpenAI 服務使用 GPT-35-Turbo and GPT-4(win版)

官方文檔 Azure OpenAI 是微軟提供的一項云服務&#xff0c;旨在將 OpenAI 的先進人工智能模型與 Azure 的基礎設施和服務相結合。通過 Azure OpenAI&#xff0c;開發者和企業可以訪問 OpenAI 的各種模型&#xff0c;如 GPT-3、Codex 和 DALL-E 等&#xff0c;并將其集成到自己…

input上傳--upload

1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>上傳文件</title><link rel"…

《C++并發編程實戰》筆記(一、二)

一、簡介 抽象損失&#xff1a;對于實現某個功能時&#xff0c;可以使用高級工具&#xff0c;也可以直接使用底層工具。這兩種方式運行的開銷差異稱為抽象損失。 二、線程管控 2.1 線程的基本控制 1. 創建線程 線程相關的管理函數和類在頭文件&#xff1a; #include <…

數據結構——線性表(C語言實現)

寫在前面&#xff1a; 在前面C語言的結構體學習中&#xff0c;我提及了鏈表的操作&#xff0c; 學習數據結構我認為還是需要對C語言的數組、函數、指針、結構體有一定的了解&#xff0c;不然對于結構體的代碼可能很難理解&#xff0c;特別是一些書籍上面用的還是偽代碼&#xf…

OpenGL筆記一之基礎窗體搭建以及事件響應

OpenGL筆記一之基礎窗體搭建以及事件響應 總結自bilibili趙新政老師的教程 code review! 文章目錄 OpenGL筆記一之基礎窗體搭建以及事件響應1.運行2.目錄結構3.main.cpp4.CMakeList.txt 1.運行 2.目錄結構 01_GLFW_WINDOW/ ├── CMakeLists.txt ├── glad.c ├── main…

Linux基于centos7指令初學3

date指令 作用&#xff1a; date指令可以查看時間 這個指令可以進行格式化 格式&#xff1a;date %想要的內容 Y&#xff1a;年份 m&#xff1a;月份 d&#xff1a;日 H&#xff1a;時 M&#xff1a;分 S&#xff1a;秒 時間分界線可以由…

GIT相關操作,推送本地分支到遠程倉庫流程記錄學習

git流程 切換到源文件夾&#xff1a;cd 源文件夾克隆遠程倉庫&#xff1a;git clone [ssh]進入項目文件夾&#xff1a;cd .\project\查看本地分支&#xff1a;git branch獲取遠程倉庫更新&#xff0c;使遠程同步&#xff1a;git fetch查看所有分支&#xff08;包括遠程分支&am…

OJ-0712

示例1&#xff1a; input 8 123 124 125 121 119 122 126 123 output 1 2 6 5 5 6 0 0示例2&#xff1a; input 2 95 100 output 1 0示例3&#xff1a; input 2 100 95 output 0 1package com.wsdcode.od;import java.util.Scanner;public class Main {public static void m…

LabVIEW比例壓力控制閥自動測試系統

開發了一套基于LabVIEW編程和PLC控制的比例控制閥自動測試系統。該系統能夠實現共軌管穩定的超高壓供給&#xff0c;自動完成比例壓力控制閥的耐久測試、流量滯環測試及壓力-流量測試。該系統操作簡便&#xff0c;具有高精度和高可靠性&#xff0c;完全滿足企業對自動化測試的需…

安裝jenkins最新版本初始化配置及使用JDK1.8構建項目詳細講解

導讀 1.安裝1.1.相關網址1.2.準備環境1.3.下載安裝 2. 配置jenkins2.1.安裝插件2.2.配置全局工具2.3.系統配置 3. 使用3.1.配置job3.2.構建 提示&#xff1a;如果只想看如何使用jdk1.8構建項目&#xff0c;直接看3.1即可。 1.安裝 1.1.相關網址 Jenkins官網&#xff1a;https…

RabbitMq如何保證消息的可靠性和穩定性

RabbitMq如何保證消息的可靠性和穩定性 rabbitMq不會百分之百讓我們的消息安全被消費&#xff0c;但是rabbitMq提供了一些機制來保證我們的消息可以被安全的消費。 消息確認 消息者在成功處理消息后可以發送確認&#xff08;ACK&#xff09;給rabbitMq&#xff0c;通知消息已…

Hadoop-25 Sqoop遷移 增量數據導入 CDC 變化數據捕獲 差量同步數據 觸發器 快照 日志

章節內容 上節我們完成了如下的內容&#xff1a; Sqoop MySQL遷移到HiveSqoop Hive遷移數據到MySQL編寫腳本進行數據導入導出測試 背景介紹 這里是三臺公網云服務器&#xff0c;每臺 2C4G&#xff0c;搭建一個Hadoop的學習環境&#xff0c;供我學習。 之前已經在 VM 虛擬機…

計算機的錯誤計算(二十九)

摘要 &#xff08;1&#xff09;討論近似值的錯誤數字個數。有時&#xff0c;遇到數字9或0, 不太好確認近似值的錯誤數字個數。&#xff08;2&#xff09;并進一步解釋確認計算機的錯誤計算&#xff08;二十八&#xff09;中一個函數值的錯誤數字個數。 理論上&#xff0c;我…

py2neo常用語句

1.連接數據庫 Neo4j服務器默認的端口號就是7474,所以本地的主機就是"http://localhost:7474" 。 默認的用戶名密碼都是neo4j&#xff0c; # 連接數據庫&#xff0c;輸入個人配置 graph Graph("http://localhost:7474//browser/", auth("neo4j"…

百日筑基第十九天-一頭扎進消息隊列2

百日筑基第十九天-一頭扎進消息隊列2 消息隊列的通訊協議 目前業界的通信協議可以分為公有協議和私有協議兩種。公有協議指公開的受到認可的具有規 范的協議&#xff0c;比如 JMS、HTTP、STOMP 等。私有協議是指根據自身的功能和需求設計的協 議&#xff0c;一般不具備通用性&…