Java 代理

来源:https://www.cnblogs.com/chy18883701161/archive/2019/08/21/11391061.html
-Advertisement-
Play Games

代理(proxy)分為2種: 靜態代理 動態代理 動態代理常用的有jdk動態代理、cglib代理。 靜態代理 1、新建User介面 2、新建實現類UserImpl 3、新建代理類UserProxy,也實現User介面,對目標對象(的方法)進行增強 4、使用代理。新建測試類Test 靜態代理的特點 代 ...


 

代理(proxy)分為2種:

  • 靜態代理
  • 動態代理    動態代理常用的有jdk動態代理、cglib代理。

 

 

靜態代理

1、新建User介面

1 public interface User {
2     void addUser();
3     void deleteUser();
4     void updateUser();
5 }

 

 

2、新建實現類UserImpl

 1 public class UserImpl implements User {
 2     @Override
 3     public void addUser() {
 4         //模擬添加用戶
 5         System.out.println("正在添加用戶");
 6         System.out.println("已添加用戶");
 7     }
 8 
 9     @Override
10     public void deleteUser() {
11         //模擬刪除用戶
12         System.out.println("正在刪除用戶");
13         System.out.println("已刪除用戶");
14     }
15 
16     @Override
17     public void updateUser() {
18         //模擬修改用戶信息
19         System.out.println("正在修改用戶信息");
20         System.out.println("已修改用戶信息");
21     }
22 }

 

 

3、新建代理類UserProxy,也實現User介面,對目標對象(的方法)進行增強

 1 public class UserProxy implements User {
 2     //定義目標對象(要代理的對象)
 3     private User user;
 4 
 5     //構造函數,初始化目標對象
 6     public UserProxy(User user){
 7         this.user=user;
 8     }
 9 
10     @Override
11     public void addUser() {
12         System.out.println("開啟事務...");  //前處理,模擬開啟事務
13         user.addUser();  //調用目標對象相應的方法
14         System.out.println("提交事務...");  //後處理,模擬提交事務
15     }
16 
17     @Override
18     public void deleteUser() {
19         System.out.println("開啟事務...");
20         user.deleteUser();
21         System.out.println("提交事務...");
22     }
23 
24     @Override
25     public void updateUser() {
26         System.out.println("開啟事務...");
27         user.updateUser();
28         System.out.println("提交事務...");
29     }
30 }

 

 

4、使用代理。新建測試類Test

1 public class Test {
2     public static void main(String[] args){
3         UserImpl user = new UserImpl(); //目標對象
4         UserProxy userProxy = new UserProxy(user);  //代理
5         userProxy.addUser();  //調用代理類的方法
6     }
7 }

 

 

靜態代理的特點

  • 代理類、目標類需要實現同一介面
  • 編譯時就已確定目標對象的類型(編譯時類型,比如上例中編譯時就知道目標對象的類型是User)
  • 目標對象只能必須是特定類型,比如上例中目標對象只能是User類型,UserProxy這個代理類只能代理User的實例。如果要代理其他類型的對象,需要再寫代理類,這就很麻煩了,一種代理只能代理一種類型,太雞肋了,我們一般不用靜態代理。

因為目標對象的類型是固定的,靜止不動的(靜態的),所以這種代理方式叫做靜態代理。

 

 

 

何謂代理?

代理是使用一個更強大的類(在原類的基礎上進行功能擴展)來代替原來的類進行工作。

比如我在使用UserImpl類時,還想使用事務、記錄日誌等做一些其他的操作,這些操作不屬於用戶類的範疇,不能封裝到UserImpl類中。這時就可以使用代理來對原來的類(方法)進行增強。代理類保留了原有類的所有功能,在此基礎上擴展了其他功能,更加強大。

被代理的類(UserImpl類)叫做目標類,實現了代理的類(UserProxy類)叫做代理類。

 

 

 

JDK動態代理

1、新建介面User

 

2、新建實現類UserImpl

 

3、新建類ProxyFactory,用於創建代理對象

 1 class ProxyFactory{
 2     //目標對象。動態代理是你傳給它什麼目標對象,它就代理什麼對象,能代理所有類型的對象,所以聲明為Object
 3     private Object target;
 4 
 5     //構造器,初始化目標對象
 6     public ProxyFactory(Object target) {
 7         this.target = target;
 8     }
 9 
10     //獲取代理對象。目標對象是Object,所以代理對象也是Object
11     public Object getProxyInstance(){
12         ClassLoader classLoader = target.getClass().getClassLoader();  //獲取目標類的類載入器。可通過目標對象獲取,也可通過目標類獲取,但目標對象聲明為Object,所以只能通過目標對象來獲取(不知道目標類)
13         Class<?>[] interfaces = target.getClass().getInterfaces();  //獲取目標類所實現的所有介面的Class對象。因為目標類可能實現了多個介面,所以用的是複數。
14         InvocationHandler invocationHandler = new InvocationHandler() {  //創建InvocationHandler介面的實例。這裡使用匿名內部類來創建
15             @Override
16             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
17                 System.out.println("前增強...");  //此處寫前增強的代碼。
18                 Object returnValue=method.invoke(target,args);  //調用目標方法
19                 System.out.println("後增強...");  //此處寫後增強的代碼
20                 return returnValue;  
21             }
22         };
23 
24 
25         Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);  //創建代理對象,參數就是上面三個參數
26         return proxyInstance;  //返回代理對象
27     }
28 }

 

 

4、新建測試類Test

1 public class Test {
2     public static void main(String[] args){
3         UserImpl user = new UserImpl(); //目標對象
4         Object obj = new ProxyFactory(user).getProxyInstance();  //創建代理對象
5         User userProxy=(User)obj;  //返回值是Object,需要強轉。只能強轉為目標類所實現的介面類型,如果強轉為目標類的類型則代理失敗(報錯)
6         userProxy.addUser();  //通過代理對象來調用方法
7     }
8 }

 

 

我們看下實現 InvocationHandler介面中的 這段代碼:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("前增強...");  //此處寫前增強的代碼
                Object returnValue=method.invoke(target,args);  //調用目標方法
                System.out.println("後增強...");  //此處寫後增強的代碼
                return returnValue;
            }

invocation是調用的意思,invoke也是調用的意思。InvocationHandler介面是用來指定如何調用目標類中的方法,按照寫的實現來調用。

 

invoke這個函數就是反射中調用指定方法的函數。

java.lang.reflect中調用某個類中的方法: Object invoke(Object proxy, Method method, Object[] args) 

第一個參數指定目標對象,第二個參數指定要調用的方法,第三個參數是所調方法需要的實參值,參數個數不確定,可寫為數組形式。invoke()的返回值就是所調方法的返回值,只不過聲明為了Object。

 

通過代理對象調用方法: userProxy.addUser();  

會自動把目標對象、要調用的方法、實參表向下傳遞給invoke(),invoke執行完以後自動把返回值向上傳回來。

 

jdk動態代理,是指用jdk自帶的東西(反射)來實現動態代理,所以又叫java動態代理,不是說要代理jdk。

 

前後增強的代碼可以放在函數中,然後在invoke()中調用對應的函數。

 

 

 

JDK動態代理的特點

  • 目標類必須繼承介面,創建代理的類無需繼承介面
  • 可以代理任何類型的類。傳什麼類型的目標類,就代理什麼類型,所有叫做動態代理。
  • 所設置的前後增強,是所有目標類中所有方法都會添加(執行)的。靜態代理可以一個方法一個方法地設置,可以根據需要具體設置每一個方法的增強。jdk動態代理是一棍子打死,全都使用相同的前後增強。

 

 

 

 

CGLIB動態代理

靜態代理、jdk動態代理都要求目標類必須實現一個或多個介面,如果目標類沒有實現介面,則代理失敗。

cglib代理不要求目標類實現介面。

 

1、如果是單獨使用cglib,需要導入cglib.jar包以及cglib的依賴包asm.jar,不推薦,這樣導包很容易出問題(cglib、asm的版本要對應)。

如果使用maven,則會自動導入依賴的asm.jar。

如果使用了spring,在spring的核心包spring-core.jar中已經內嵌了cglib,不必再導包。

 

2、新建類User,不必實現任何介面

 

3、新建創建代理對象的類ProxyFactory,需實現MethodInterceptor介面

 1 class ProxyFactory implements MethodInterceptor {
 2     //目標對象。聲明為Object
 3     private Object target;
 4 
 5     //構造器,初始化目標對象
 6     public ProxyFactory(Object target) {
 7         this.target = target;
 8     }
 9 
10     //給目標對象創建一個代理對象
11     public Object getProxyInstance(){
12         Enhancer en = new Enhancer();  //創建工具類對象
13         en.setSuperclass(target.getClass());   //設置父類
14         en.setCallback(this);  //設置回調函數。這句代碼是給代理類對象設置攔截intercept()。前面只是繼承了目標類,此處設置攔截(在攔截中實現增強、調用目標方法)
15         return en.create();   //創建代理對象並返回
16 
17     }
18 
19     //攔截
20     @Override   //cglib代理仍是在反射的基礎上擴展而來的,所以參數和反射調用方法的參數差不多目標對象、背調方法、實參表、方法代理
21     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
22         System.out.println("前增強...");  //前增強代碼
23         Object returnValue = method.invoke(target, objects);  //傳入目標對象、實參表。
24         System.out.println("後增強...");  //後增強代碼
25         return returnValue;  //返回被調函數的返回值,是作為Object返回的
26     }
27 
28 }

 

 

4、新建測試類Test

1 public class Test {
2     public static void main(String[] args){
3         User user = new User(); //目標對象
4         Object obj = new ProxyFactory(user).getProxyInstance();  //創建代理對象
5         User userProxy=(User) obj;  //返回值是Object,需要強轉為目標類對象。
6         userProxy.addUser();  //通過代理對象來調用方法
7     }
8 }

 

 

 

CGLIB動態代理的特點

  • 可以代理任何類,但目標類不能用final修飾,因為代理類要繼承目標類;目標類中的方法不能使用final(要改寫,前後增強)、static(要求是實例方法)修飾。
  • 目標類可以實現介面,也可以不實現任何介面。
  • 目標類中的所有方法都會被增強

 

如果目標類實現了介面,使用jdk、cglib都行;如果目標類沒有實現任何介面,則使用cglib。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 系統設計:關於高可用系統的一些技術方案 可靠的系統是業務穩定、快速發展的基石。那麼,如何做到系統高可靠、高可用呢?下麵從技術方面介紹幾種提高系統可靠性、可用性的方法。 擴展 擴展是最常見的提升系統可靠性的方法,系統的擴展可以避免單點故障,即一個節點出現了問題造成整個系統無法正常工作。換一個角度講,一 ...
  • “類”是面向對象中一個重要的概念,有了類我們才能去討論什麼是封裝、繼承、多態。 什麼是類?先看下麵這張圖片。 現在你看到這張圖片後,根據圖片中表達的內容造一個句子吧。思考中........,OK,把自己想好的句子記在心裡,然後看下麵我的句子。o(* ̄︶ ̄*)o “一隻動物被兩個人往一個家電裡面裝” ...
  • 1.InteliJ IDEA設置快捷鍵 1.1. IDEA快捷鍵修改—代碼提示 IDEA中當現有的快捷鍵被系統中其他軟體(比如輸入法)占用時,我們可以自定義修改快捷鍵。比如,IDEA中的代碼自動提示快捷鍵和eclipse的不一樣,不是Alt + /,此時可以通過修改快捷鍵來設置。 修改方式: ①選擇 ...
  • 利用 Spring Boot Admin 進行項目監控管理 一、Spring Boot Admin 是什麼 Spring Boot Admin (SBA) 是一個社區開源項目,用於管理和監視 Spring Boot 應用程式。應用程式通過 的方式註冊到 Spring Boot 管理客戶端,或者通過 ...
  • 一、聯繫一個if語句 二、接受用戶輸入 三、簡寫括弧 四、源碼: d19_user_input_and_omit_brace.java 地址:https://github.com/ruigege66/Java/blob/master/d19_user_input_and_omit_brace.jav ...
  • 自1998年 JDK 1.0(Java 1.0) 發佈以來,Java 已經受到了學生、項目經理和程式員等一大批活躍用戶的歡迎。這一語言極富活力,不斷被用在大大小小的項目里。從 Java 1.1(1997年) 一直到 Java 7(2011年),Java 通過增加新功能,不斷得到良好的升級。Java ...
  • 基本類: AtomicInteger AtomicLong "AtomicBoolean" 數組類型: AtomicIntegerArray AtomicLongArray AtomicReferenceArray 介紹 由於在多線程條件下,如果對共用變數修改容易造成數據不一致的情況,所以對於共用變 ...
  • 最近一個朋友,在謀求架構師崗位的工作,經歷了魔都的一批互聯網公司的洗禮,讓他把面試經歷整理了一下,給大家一些經驗吧,希望各位後面去這些公司面試的時候,能有些心理準備。 還唄 地點:2號線金科路地鐵站(長泰廣場A座) 環境:環境還不錯,裝修偏簡約風,工位是互聯網公司那種排排坐。 投遞方式:拉勾網 面試 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...