<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                編程語言通常有各種不同的分類角度,動態類型和靜態類型就是其中一種分類角度,簡單區分就是語言類型信息是在運行時檢查,還是編譯期檢查。 與其近似的還有一個對比,就是所謂強類型和弱類型,就是不同類型變量賦值時,是否需要顯式地(強制)進行類型轉換。 那么,如何分類 Java 語言呢?通常認為,Java 是靜態的強類型語言,但是因為提供了類似反射等機制,也具備了部分動態類型語言的能力。 言歸正傳,今天我要問你的問題是,談談 Java 反射機制,動態代理是基于什么原理? ## 典型回答 反射機制是 Java 語言提供的一種基礎功能,賦予程序在運行時自省(introspect,官方用語)的能力。通過反射我們可以直接操作類或者對象,比如獲取某個對象的類定義,獲取類聲明的屬性和方法,調用方法或者構造對象,甚至可以運行時修改類定義。 動態代理是一種方便運行時動態構建代理、動態處理代理方法調用的機制,很多場景都是利用類似機制做到的,比如用來包裝 RPC 調用、面向切面的編程(AOP)。 實現動態代理的方式很多,比如 JDK 自身提供的動態代理,就是主要利用了上面提到的反射機制。還有其他的實現方式,比如利用傳說中更高性能的字節碼操作機制,類似 ASM、cglib(基于 ASM)、Javassist 等。 ## 考點分析 這個題目給我的第一印象是稍微有點誘導的嫌疑,可能會下意識地以為動態代理就是利用反射機制實現的,這么說也不算錯但稍微有些不全面。功能才是目的,實現的方法有很多。總的來說,這道題目考察的是 Java 語言的另外一種基礎機制: 反射,它就像是一種魔法,引入運行時自省能力,賦予了 Java 語言令人意外的活力,通過運行時操作元數據或對象,Java 可以靈活地操作運行時才能確定的信息。而動態代理,則是延伸出來的一種廣泛應用于產品開發中的技術,很多繁瑣的重復編程,都可以被動態代理機制優雅地解決。 從考察知識點的角度,這道題涉及的知識點比較龐雜,所以面試官能夠擴展或者深挖的內容非常多,比如: * 考察你對反射機制的了解和掌握程度。 * 動態代理解決了什么問題,在你業務系統中的應用場景是什么? * JDK 動態代理在設計和實現上與 cglib 等方式有什么不同,進而如何取舍? 這些考點似乎不是短短一篇文章能夠囊括的,我會在知識擴展部分盡量梳理一下。 ## 知識擴展 1. 反射機制及其演進 對于 Java 語言的反射機制本身,如果你去看一下 java.lang 或 java.lang.reflect 包下的相關抽象,就會有一個很直觀的印象了。Class、Field、Method、Constructor 等,這些完全就是我們去操作類和對象的元數據對應。反射各種典型用例的編程,相信有太多文章或書籍進行過詳細的介紹,我就不再贅述了,至少你需要掌握基本場景編程,這里是官方提供的參考文檔:https://docs.oracle.com/javase/tutorial/reflect/index.html 。 關于反射,有一點我需要特意提一下,就是反射提供的 AccessibleObject.setAccessible?(boolean flag)。它的子類也大都重寫了這個方法,這里的所謂 accessible 可以理解成修飾成員的 public、protected、private,這意味著我們可以在運行時修改成員訪問限制! setAccessible 的應用場景非常普遍,遍布我們的日常開發、測試、依賴注入等各種框架中。比如,在 O/R Mapping 框架中,我們為一個 Java 實體對象,運行時自動生成 setter、getter 的邏輯,這是加載或者持久化數據非常必要的,框架通常可以利用反射做這個事情,而不需要開發者手動寫類似的重復代碼。 另一個典型場景就是繞過 API 訪問控制。我們日常開發時可能被迫要調用內部 API 去做些事情,比如,自定義的高性能 NIO 框架需要顯式地釋放 DirectBuffer,使用反射繞開限制是一種常見辦法。 但是,在 Java 9 以后,這個方法的使用可能會存在一些爭議,因為 Jigsaw 項目新增的模塊化系統,出于強封裝性的考慮,對反射訪問進行了限制。Jigsaw 引入了所謂 Open 的概念,只有當被反射操作的模塊和指定的包對反射調用者模塊 Open,才能使用 setAccessible;否則,被認為是不合法(illegal)操作。如果我們的實體類是定義在模塊里面,我們需要在模塊描述符中明確聲明: ``` module MyEntities { // Open for reflection opens com.mycorp to java.persistence; } ``` 因為反射機制使用廣泛,根據社區討論,目前,Java 9 仍然保留了兼容 Java 8 的行為,但是很有可能在未來版本,完全啟用前面提到的針對 setAccessible 的限制,即只有當被反射操作的模塊和指定的包對反射調用者模塊 Open,才能使用 setAccessible,我們可以使用下面參數顯式設置。 ``` --illegal-access={ permit | warn | deny } ``` 2. 動態代理 前面的問題問到了動態代理,我們一起看看,它到底是解決什么問題? 首先,它是一個代理機制。如果熟悉設計模式中的代理模式,我們會知道,代理可以看作是對調用目標的一個包裝,這樣我們對目標代碼的調用不是直接發生的,而是通過代理完成。其實很多動態代理場景,我認為也可以看作是裝飾器(Decorator)模式的應用,我會在后面的專欄設計模式主題予以補充。 通過代理可以讓調用者與實現者之間解耦。比如進行 RPC 調用,框架內部的尋址、序列化、反序列化等,對于調用者往往是沒有太大意義的,通過代理,可以提供更加友善的界面。 代理的發展經歷了靜態到動態的過程,源于靜態代理引入的額外工作。類似早期的 RMI 之類古董技術,還需要 rmic 之類工具生成靜態 stub 等各種文件,增加了很多繁瑣的準備工作,而這又和我們的業務邏輯沒有關系。利用動態代理機制,相應的 stub 等類,可以在運行時生成,對應的調用操作也是動態完成,極大地提高了我們的生產力。改進后的 RMI 已經不再需要手動去準備這些了,雖然它仍然是相對古老落后的技術,未來也許會逐步被移除。 這么說可能不夠直觀,我們可以看 JDK 動態代理的一個簡單例子。下面只是加了一句 print,在生產系統中,我們可以輕松擴展類似邏輯進行診斷、限流等。 ``` public class MyDynamicProxy { public static void main (String[] args) { HelloImpl hello = new HelloImpl(); MyInvocationHandler handler = new MyInvocationHandler(hello); // 構造代碼實例 Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler); // 調用代理方法 proxyHello.sayHello(); } } interface Hello { void sayHello(); } class HelloImpl implements Hello { @Override public void sayHello() { System.out.println("Hello World"); } } 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 { System.out.println("Invoking sayHello"); Object result = method.invoke(target, args); return result; } } ``` 上面的 JDK Proxy 例子,非常簡單地實現了動態代理的構建和代理操作。首先,實現對應的 InvocationHandler;然后,以接口 Hello 為紐帶,為被調用目標構建代理對象,進而應用程序就可以使用代理對象間接運行調用目標的邏輯,代理為應用插入額外邏輯(這里是 println)提供了便利的入口。 從 API 設計和實現的角度,這種實現仍然有局限性,因為它是以接口為中心的,相當于添加了一種對于被調用者沒有太大意義的限制。我們實例化的是 Proxy 對象,而不是真正的被調用類型,這在實踐中還是可能帶來各種不便和能力退化。 如果被調用者沒有實現接口,而我們還是希望利用動態代理機制,那么可以考慮其他方式。我們知道 Spring AOP 支持兩種模式的動態代理,JDK Proxy 或者 cglib,如果我們選擇 cglib 方式,你會發現對接口的依賴被克服了。 cglib 動態代理采取的是創建目標類的子類的方式,因為是子類化,我們可以達到近似使用被調用者本身的效果。在 Spring 編程中,框架通常會處理這種情況,當然我們也可以顯式指定。關于類似方案的實現細節,我就不再詳細討論了。 那我們在開發中怎樣選擇呢?我來簡單對比下兩種方式各自優勢。 JDK Proxy 的優勢: * 最小化依賴關系,減少依賴意味著簡化開發和維護,JDK 本身的支持,可能比 cglib 更加可靠。 * 平滑進行 JDK 版本升級,而字節碼類庫通常需要進行更新以保證在新版 Java 上能夠使用。 * 代碼實現簡單。 基于類似 cglib 框架的優勢: * 有的時候調用目標可能不便實現額外接口,從某種角度看,限定調用者實現接口是有些侵入性的實踐,類似 cglib 動態代理就沒有這種限制。 * 只操作我們關心的類,而不必為其他相關類增加工作量。 * 高性能。 另外,從性能角度,我想補充幾句。記得有人曾經得出結論說 JDK Proxy 比 cglib 或者 Javassist 慢幾十倍。坦白說,不去爭論具體的 benchmark 細節,在主流 JDK 版本中,JDK Proxy 在典型場景可以提供對等的性能水平,數量級的差距基本上不是廣泛存在的。而且,反射機制性能在現代 JDK 中,自身已經得到了極大的改進和優化,同時,JDK 很多功能也不完全是反射,同樣使用了 ASM 進行字節碼操作。 我們在選型中,性能未必是唯一考量,可靠性、可維護性、編程工作量等往往是更主要的考慮因素,畢竟標準類庫和反射編程的門檻要低得多,代碼量也是更加可控的,如果我們比較下不同開源項目在動態代理開發上的投入,也能看到這一點。 動態代理應用非常廣泛,雖然最初多是因為 RPC 等使用進入我們視線,但是動態代理的使用場景遠遠不僅如此,它完美符合 Spring AOP 等切面編程。我在后面的專欄還會進一步詳細分析 AOP 的目的和能力。簡單來說它可以看作是對 OOP 的一個補充,因為 OOP 對于跨越不同對象或類的分散、糾纏邏輯表現力不夠,比如在不同模塊的特定階段做一些事情,類似日志、用戶鑒權、全局性異常處理、性能監控,甚至事務處理等,你可以參考下面這張圖。 ![](https://img.kancloud.cn/ba/9a/ba9a5b6228b188f5b9b15017e29a302b_800x575.png) AOP 通過(動態)代理機制可以讓開發者從這些繁瑣事項中抽身出來,大幅度提高了代碼的抽象程度和復用度。從邏輯上來說,我們在軟件設計和實現中的類似代理,如 Facade、Observer 等很多設計目的,都可以通過動態代理優雅地實現。 今天我簡要回顧了反射機制,談了反射在 Java 語言演進中正在發生的變化,并且進一步探討了動態代理機制和相關的切面編程,分析了其解決的問題,并探討了生產實踐中的選擇考量。 ## 一課一練 關于今天我們討論的題目你做到心中有數了嗎?留一道思考題給你,你在工作中哪些場景使用到了動態代理?相應選擇了什么實現技術?選擇的依據是什么?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看