代理(一)靜態代理,動態代理

来源:https://www.cnblogs.com/huskyui/archive/2018/12/23/10165003.html
-Advertisement-
Play Games

昨天面試碰到了面試官問spring的時候,問完ioc,然後下個問題我以為是aop,後來才聽清是動態代理,感覺回答的不是太好,也是好久不接觸 1.靜態代理 代理模式也就委托模式。 三個角色: 1. Subject抽象主題角色:介面 2. RealSubject具體主題角色:介面的實現類,業務邏輯的具體 ...


昨天面試碰到了面試官問spring的時候,問完ioc,然後下個問題我以為是aop,後來才聽清是動態代理,感覺回答的不是太好,也是好久不接觸

1.靜態代理

代理模式也就委托模式。

三個角色:

1. Subject抽象主題角色:介面
2. RealSubject具體主題角色:介面的實現類,業務邏輯的具體執行者
3. Proxy代理主題角色:也叫做委托類,代理類。它負責對真實角色的應用,負責真實角色前後前後做預處理和善後處理。

 在這裡面Subject是一個藉口,裡面有一個Request()方法,RealSubject類實現了Subject這個介面,並且實現了request()方法,而在Proxy也實現了Subject介面,proxy裡面還有一個變數Subject,可以通過構造函數將RealSubject註入,在實現request方法時,其實是調用的是Realsubject裡面的request方法,在調用request()方法前後可以調用一些其他的方法。

下麵是具體的代碼

Subject.java

public interface Subject {
    //定義一個方法
    public void request();
}

RealSubject.java

public class RealSubject implements Subject {

    @Override
    public void request() {
        //業務邏輯處理
    }
}

Proxy.java

public class Proxy implements Subject {
    private Subject subject = null;

    public Proxy(Subject subject){
        this.subject = subject;
    }

    @Override
    public void request() {
        before();
        subject.request();
        after();
    }

    public void before(){
        //do something
    }

    public void after(){
        //do something
    }
}

代理模式的使用場景
為什麼要使用代理呢?想想現實世界吧,打官司為什麼要找個律師?因為你不想參與中間過程的是是非非,只要完成自己的答辯就行了,其他的比如事前調查、事後調查都由律師搞定,這就是為了減輕你的負擔。代理模式的使用場景非常多,可以看見Spring aop,這是非常典型的動態代理

二:動態代理

動態代理是在實現階段不用關心代理誰,而在運行階段才指定代理哪一個對象。相對來說,自己寫代理類的方式就叫靜態代理

劃重點,在運行階段指定哪一個對象

具體代碼,邏輯待會再說

抽象主題

public interface Subject {
    /**
     * //業務操作
     * @param str
     */
    void doSomething(String str);


    /**
     * 吃
     * */
    void eat(String str);
}

真實主題

public class RealSubject implements Subject {
    @Override
    public void doSomething(String str) {
        System.out.println("do something!---->" + str);
    }

    @Override
    public void eat(String str) {
        System.out.println("今天晚上吃"+str);
    }
}

動態代理的handler類

public class MyInvocationHandler implements InvocationHandler {


    /**
     * 被代理的對象
     */
    private Object target;

    public MyInvocationHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //log Before
        System.out.println("log before:"+"method: "+method.getName()+ "return :"+method.getReturnType().getSimpleName());
        Object result = method.invoke(target,args);
        System.out.println("log after: "+new Date(System.currentTimeMillis()));
        return result;
    }
}

動態代理類

public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler){
        //返回目標,並返回結果
        return (T) Proxy.newProxyInstance(loader,interfaces,handler);
    }
}

動態代理的場景類

public class Client {
    public static void main(String[] args) throws IOException {
        //定義一個代理類
        Subject subject = new RealSubject();
        //定義一個Handler
        InvocationHandler handler = new MyInvocationHandler(subject);
        //定義主題的代理
        Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),handler);

        //代理的行為

        System.out.println(proxy.getClass().getCanonicalName());
        proxy.doSomething("finish");

        proxy.eat("香鍋里辣家的麻辣香鍋");

        //此處可以將
        byte[] proxyClass = ProxyGenerator.generateProxyClass(proxy.getClass()
                .getSimpleName(), proxy.getClass().getInterfaces());
        //將位元組碼文件保存到D盤,文件名為$Proxy0.class
        FileOutputStream outputStream = new FileOutputStream(new File(
                "d:\\$Proxy0.class"));
        outputStream.write(proxyClass);
        outputStream.flush();
        outputStream.close();

    }
}

分析過程:

 Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),handler);

在我們使用proxy.doSomthing(args);
proxy.eat(args);*
***我們可以想象出,DynamicProxy.newProxyInstance(args)這個方法返回了一個proxy對象***
我們可以看到是調用了這個方法
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }最後這個方法會在運行中生成一個

類,名字叫:com.sun.proxy.$Proxy0
而我們並沒有找到這個類,可以通過

ProxyGenerator.generateProxyClass

將這個運行中生成的類列印出來,,我們來看看,這個類是什麼樣子的
$Proxy0.class,反編譯

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void doSomething(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void eat(String var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.husky.dynamicproxy.Subject").getMethod("doSomething", Class.forName("java.lang.String"));
            m4 = Class.forName("com.husky.dynamicproxy.Subject").getMethod("eat", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

首先$Proxy0這個類extends Proxy implements Subject
有一個靜態代碼塊,會反射生成Method類,也就是給 $Proxy這個類的變數賦值
舉例我在調用proxy.doSomething()這個方法時
我們其實是調用

 public final void doSomething(String var1) throws  {
            try {
                super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }

調用的是super.h.invoke(this, m3, new Object[]{var1});
這個h也就是實現InvocationHandler介面的MyInvocationHandler類。將這個$Proxy0,m3, new Object[]{var1}這些變數傳過去

具體代碼:https://github.com/pompeii666/proxy

 


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

-Advertisement-
Play Games
更多相關文章
  • 一、spring概述 Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其他各層的松耦合問題,因此它將面向介面的編程思想貫穿整個系統應用。Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/E ...
  • 偶然看到一篇100多行實現SpringMvc的博客,閱讀後整理加實現出來。大家共勉!(紙上得來終覺淺,絕知此事要躬行。) 實現Spring的部分。 代碼解析如下: 1、首先創建Servelt,繼承HttpServlet。覆蓋init/doGet/doPost方法。 2、配置web.xml 2.1、配 ...
  • Python類練習 定義一個類 列印Point: Point實例化為一個p1: 我們直接列印 結果為 __main__.Point` 給實例p1添加屬性 (3, 4) 原點(0, 0)與p1之間的距離:5.0 (3, 4) 定義矩形 實例化一個例子r1 (100, 200) 列印矩形的寬度width ...
  • 這篇文章一起來回顧複習下spring的事務操作.事務是spring的重點, 也是面試的必問知識點之一. 說來這次面試期間,也問到了我,由於平時用到的比較少,也沒有關註過這一塊的東西,所以回答的不是特別好,所以借這一篇文章來回顧總結一下,有需要的朋友,也可以點贊收藏一下,複習一下這方面的知識,為年後的 ...
  • 我們可以在application.properties中配置自定義的屬性值,為了獲取這些值,我們可以使用spring提供的@value註解,還可以使用springboot提供的@ConfigurationProperties註解非常方便的完成屬性值的註入。 ...
  • 剛開始接觸 python 的時候,對 python 中的 wargs (可變參數) 和 kwargs (關鍵字參數)的理解不是很透徹,看了一下 《Explore Python》一書,裡面對這一部分的描述相對淺顯易懂, 這裡依據個人理解進行相關總結。 可變參數( args) 對於可變參數可以聯想到 C ...
  • 一.函數調用 在python中內置了很多函數,我們可以直接調用 。想要調用函數首先要知道函數的名稱及包含的參數,還可以通過查看python官方的文檔:https://docs.python.org/3/library/functions.html 就拿abs()函數來舉例 用來返回數值的絕對值 >> ...
  • 上面的mixins、generics都是rest_framework里的模塊,我們可以繼承其中的某些類,達到代碼量減少的效果,這裡充分體現出了面向對象的繼承 一、mixins模塊 二、generics模塊 三、通過一個簡單的例子,順帶寫mixins,generics的用處 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...