1. 程式人生 > >InvocationHandler和Proxy(Class)的動態代理機制詳解

InvocationHandler和Proxy(Class)的動態代理機制詳解

在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。

在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文件是怎麼樣對這兩個類進行描述的:

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的例項都關聯到了一個handler,當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。我們來看看InvocationHandler這個介面的唯一一個方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我們看到這個方法一共接受三個引數,那麼這三個引數分別代表什麼呢?

Object invoke(Object proxy, Method method, Object[] args) throws
Throwable proxy:  指代我們所代理的那個真實物件 method:  指代的是我們所要呼叫真實物件的某個方法的Method物件 args:  指代的是呼叫真實物件某個方法時接受的引數

如果不是很明白,等下通過一個例項會對這幾個引數進行更深的講解。

接下來我們來看看Proxy這個類:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

Proxy這個類的作用就是用來動態建立一個代理物件的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

這個方法的作用就是得到一個動態的代理物件,其接收三個引數,我們來看看這三個引數所代表的含義:

複製程式碼
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入

interfaces:  一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了

h:  一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上
複製程式碼

好了,在介紹完這兩個介面(類)以後,我們來通過一個例項來看看我們的動態代理模式是什麼樣的:

首先我們定義了一個Subject型別的介面,為其聲明瞭兩個方法:

public interface Subject
{
    public void rent();
    
    public void hello(String str);
}

接著,定義了一個類來實現這個介面,這個類就是我們的真實物件,RealSubject類:

複製程式碼
public class RealSubject implements Subject
{
    @Override
    public void rent()
    {
        System.out.println("I want to rent my house");
    }
    
    @Override
    public void hello(String str)
    {
        System.out.println("hello: " + str);
    }
}
複製程式碼

下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個介面,因此我們這個動態代理類也不例外:

複製程式碼
public class DynamicProxy implements InvocationHandler
{
    // 這個就是我們要代理的真實物件
    private Object subject;
    
    //    構造方法,給我們要代理的真實物件賦初值
    public DynamicProxy(Object subject)
    {
        this.subject = subject;
    }
    
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真實物件前我們可以新增一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        //    當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
        method.invoke(subject, args);
        
        //  在代理真實物件後我們也可以新增一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }

}
複製程式碼

最後,來看看我們的Client類:

複製程式碼
public class Client
{
    public static void main(String[] args)
    {
        //    我們要代理的真實物件
        Subject realSubject = new RealSubject();

        //    我們要代理哪個真實物件,就將該物件傳進去,最後是通過該真實物件來呼叫其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 通過Proxy的newProxyInstance方法來建立我們的代理物件,我們來看看其三個引數
         * 第一個引數 handler.getClass().getClassLoader() ,我們這裡使用handler這個類的ClassLoader物件來載入我們的代理物件
         * 第二個引數realSubject.getClass().getInterfaces(),我們這裡為代理物件提供的介面是真實物件所實行的介面,表示我要代理的是該真實物件,這樣我就能呼叫這組介面中的方法了
         * 第三個引數handler, 我們這裡將這個代理物件關聯到了上方的 InvocationHandler 這個物件上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        
        System.out.println(subject.getClass().getName());
        subject.rent();
        subject.hello("world");
    }
}
複製程式碼

我們先來看看控制檯的輸出:

複製程式碼
$Proxy0
before rent house Method:
public abstract void com.xiaoluo.dynamicproxy.Subject.rent() I want to rent my house after rent house
before rent house Method:
public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String) hello: world after rent house
複製程式碼

我們首先來看看 $Proxy0 這東西,我們看到,這個東西是由 System.out.println(subject.getClass().getName()); 這條語句打印出來的,那麼為什麼我們返回的這個代理物件的類名是這樣的呢?

Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);

可能我以為返回的這個代理物件會是Subject型別的物件,或者是InvocationHandler的物件,結果卻不是,首先我們解釋一下為什麼我們這裡可以將其轉化為Subject型別的物件?原因就是在newProxyInstance這個方法的第二個引數上,我們給這個代理物件提供了一組什麼介面,那麼我這個代理物件就會實現了這組介面,這個時候我們當然可以將這個代理物件強制型別轉化為這組介面中的任意一個,因為這裡的介面是Subject型別,所以就可以將其轉化為Subject型別了

同時我們一定要記住,通過 Proxy.newProxyInstance 建立的代理物件是在jvm執行時動態生成的一個物件,它並不是我們的InvocationHandler型別,也不是我們定義的那組介面的型別,而是在執行是動態生成的一個物件,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示物件的標號

接著我們來看看這兩句 

subject.rent();
subject.hello("world");

這裡是通過代理物件來呼叫實現的那種介面中的方法,這個時候程式就會跳轉到由這個代理物件關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 物件又接受了一個 RealSubject型別的引數,表示我要代理的就是這個真實物件,所以此時就會呼叫 handler 中的invoke方法去執行:

複製程式碼
public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真實物件前我們可以新增一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        //    當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
        method.invoke(subject, args);
        
        //  在代理真實物件後我們也可以新增一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }
複製程式碼

我們看到,在真正通過代理物件來呼叫真實物件的方法的時候,我們可以在該方法前後新增自己的一些操作,同時我們看到我們的這個 method 物件是這樣的:

public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

正好就是我們的Subject介面中的兩個方法,這也就證明了當我通過代理物件來呼叫方法的時候,起實際就是委託由其關聯到的 handler 物件的invoke方法中來呼叫,並不是自己來真實呼叫,而是通過代理的方式來呼叫的。

這就是我們的java動態代理機制

相關推薦

InvocationHandlerProxy(Class)的動態代理機制

在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就

(轉)java的動態代理機制

spring throw system urn log enc before 代理類 三個參數 原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在學習Spring的時候,我們知道Spring主要

java的動態代理機制

following space h264 owin ipc ava smr hot lower Oq耗喊都自稚剿8斷0ohttp://shequ.docin.com/txqq_073ec59204 掀承U智泛纖06劣z粕05寡http://www.facebolw.com

【轉】java的動態代理機制

bar 同時 @override returns 復制 exce ins com hello 在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴註入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎

_141_Java_java的動態代理機制

在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就

Java動態代理機制

 在學習spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是Java的動態代理機制,所以本篇

Java動態代理機制(JDK動態代理與CGLIB動態代理區別)

代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。在講述動態代理前,我們先通過一個例子瞭解一下什麼是靜態代理,這裡以事務控制為例。 1.靜態

java代理機制(動態代理原理解析,簡單易懂!)

一.代理機制概念 1.代理機制是一種設計模式,分為靜態代理 與動態代理. 2.特徵:代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。 代理類的物件本身並不真正實現服務,而是通過呼叫委託類的物件的相關方法,來提供特

Java動態代理 深度

實現 接下來 href 新建 結構 str 如果 cat 子類 代理模式是設計模式中非常重要的一種類型,而設計模式又是編程中非常重要的知識點,特別是在業務系統的重構中,更是有舉足輕重的地位。代理模式從類型上來說,可以分為靜態代理和動態代理兩種類型。 今天我將用非常簡單易懂的

基於JDK的動態代理技術

end course log 些許 private provide url 模仿 ade 雖然對於Spring的基本思想Aop是基於動態代理和CGlib這一點很早就有所認識,但是什麽是動態代理卻不甚清楚。為了對Spring加深理解,我覺得好好學習一下java

java中動態代理原始碼

在研究程式碼之前,我們先看看一個例項,然後我們根據例項來進行原始碼研究。 例項程式碼如下: package com.jd.dynamicproxy.dynamicproxy; import java.io.Serializable; import java.lang.reflect.Inv

jvm原理(34)虛方法表與動態分派機制

編寫程式碼: public class MyTest7 { public static void main(String[] args) { Animal animal = new Animal(); Animal

動態代理知識

動態代理實現的兩種方式 給動態代理舉個栗子:例如我們平時買膝上型電腦,很多時候都是不從廠家拿貨,而是通過經銷商買電腦。代理

JDK的Proxy技術實現AOP,InvocationHandlerProxy——Spring AOP(三)

上一章已經講到了想要完成AOP的設計原理以及要實現AOP功能,得需要使用代理模式: 本章就介紹一個實現動態代理的兩種方式之一——JDK中的Proxy技術 AOP實現原理(使用JDK中的Proxy技術實現AOP功能,InvocationHandler和Proxy(Class)詳解

Java代理動態代理機制分析應用

一、概述        代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。根據代理類的生成時間不同可以將代理分為靜態代理和動態代理兩

java設定模式---代理模式--動態代理模式cglib代理模式

代理模式使用場景 代理模式的定義:什麼是代理模式呢?代理模式是常用的Java設計模式,它的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理消息等。代理類和委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物

java 動態代理機制的理解分析

引言 Java 動態代理機制的出現,使得 Java 開發人員不用手工編寫代理類,只要簡單地指定一組介面及委託類物件,便能動態地獲得代理類。代理類會負責將所有的方法呼叫分派到委託物件上反射執行,在分派執行的過程中,開發人員還可以按需調整委託類物件及其功能,這是一套非常靈活有彈

代理模式(靜態代理動態代理的區別以及聯系)

sys 事務處理 getname 沒有 面向接口編程 簡單 關註 不知道 正是 原文鏈接:https://www.cnblogs.com/takumicx/p/9285230.html 1. 前言 代理模式可以說是生活中處處可見。比如說在攜程上定火車票,攜程在這裏就起到

JAVA動態代理機制解析

定義 book lang 並不是 stat 控制 () highlight 什麽 1. 概述  首先,我們來思考如下兩個問題:  什麽是代理模式?為什麽要使用代理模式?   簡單總結一下,所謂的代理模式就是在原有的服務上多加一個占位,通過這個占位去控制服務的訪問。通過代理模

JAVA的動態代理機制

一點 存在 邏輯 asm cglib tor 詳細介紹 類函數 jdk AOP的攔截功能是由java中的動態代理來實現的。說白了,就是在目標類的基礎上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函數執行之前,或者目標類函數執行之後,或者在目標類函數拋出異常時候執