1. 程式人生 > >java動態代理中的invoke方法是如何被自動呼叫的

java動態代理中的invoke方法是如何被自動呼叫的

轉載宣告:本文轉載至 zcc_0015的專欄

一、動態代理與靜態代理的區別。 (1)Proxy類的程式碼被固定下來,不會因為業務的逐漸龐大而龐大; (2)可以實現AOP程式設計,這是靜態代理無法實現的; (3)解耦,如果用在web業務下,可以實現資料層和業務層的分離。 (4)動態代理的優勢就是實現無侵入式的程式碼擴充套件。 靜態代理這個模式本身有個大問題,如果類方法數量越來越多的時候,代理類的程式碼量是十分龐大的。所以引入動態代理來解決此類問題

二、動態代理

Java中動態代理的實現,關鍵就是這兩個東西:Proxy、InvocationHandler,下面從InvocationHandler介面中的invoke方法入手,簡單說明一下Java如何實現動態代理的。 
首先,invoke方法的完整形式如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
	method.invoke(obj, args);  
	return null;  
}

首先猜測一下,method是呼叫的方法,即需要執行的方法;args是方法的引數;proxy,這個引數是什麼?以上invoke()方法的實現即是比較標準的形式,我們看到,這裡並沒有用到proxy引數。檢視JDK文件中Proxy的說明,如下:
A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, 
passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.  
由此可以知道以上的猜測是正確的,同時也知道,proxy引數傳遞的即是代理類的例項。

為了方便說明,這裡寫一個簡單的例子來實現動態代理。

 //抽象角色(動態代理只能代理介面)  
public interface Subject {  
      
    public void request();  
}

 //真實角色:實現了Subject的request()方法  
public class RealSubject implements Subject{  
      
    public void request(){  
        System.out.println("From real subject.");  
    }  
} 

    //實現了InvocationHandler  
    public class DynamicSubject implements InvocationHandler  
    {  
        private Object obj;//這是動態代理的好處,被封裝的物件是Object型別,接受任意型別的物件  
      
        public DynamicSubject()  
        {  
        }  
      
        public DynamicSubject(Object obj)  
        {  
            this.obj = obj;  
        }  
      
        //這個方法不是我們顯示的去呼叫  
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
        {  
            System.out.println("before calling " + method);  
      
            method.invoke(obj, args);  
      
            System.out.println("after calling " + method);  
      
            return null;  
        }  
      
    }  

    //客戶端:生成代理例項,並呼叫了request()方法  
    public class Client {  
      
        public static void main(String[] args) throws Throwable{  
            // TODO Auto-generated method stub  
      
            Subject rs=new RealSubject();//這裡指定被代理類  
            InvocationHandler ds=new DynamicSubject(rs);  
            Class<?> cls=rs.getClass();  
              
            //以下是一次性生成代理  
              
            Subject subject=(Subject) Proxy.newProxyInstance(  
                    cls.getClassLoader(),cls.getInterfaces(), ds);  
              
            //這裡可以通過執行結果證明subject是Proxy的一個例項,這個例項實現了Subject介面  
            System.out.println(subject instanceof Proxy);  
              
            //這裡可以看出subject的Class類是$Proxy0,這個$Proxy0類繼承了Proxy,實現了Subject介面  
            System.out.println("subject的Class類是:"+subject.getClass().toString());  
              
            System.out.print("subject中的屬性有:");  
              
            Field[] field=subject.getClass().getDeclaredFields();  
            for(Field f:field){  
                System.out.print(f.getName()+", ");  
            }  
              
            System.out.print("\n"+"subject中的方法有:");  
              
            Method[] method=subject.getClass().getDeclaredMethods();  
              
            for(Method m:method){  
                System.out.print(m.getName()+", ");  
            }  
              
            System.out.println("\n"+"subject的父類是:"+subject.getClass().getSuperclass());  
              
            System.out.print("\n"+"subject實現的介面是:");  
              
            Class<?>[] interfaces=subject.getClass().getInterfaces();  
              
            for(Class<?> i:interfaces){  
                System.out.print(i.getName()+", ");  
            }  
      
            System.out.println("\n\n"+"執行結果為:");  
            subject.request();  
        }  
    }  

    執行結果如下:此處省略了包名,***代替  
    true  
    subject的Class類是:class $Proxy0  
    subject中的屬性有:m1, m3, m0, m2,   
    subject中的方法有:request, hashCode, equals, toString,   
    subject的父類是:class java.lang.reflect.Proxy  
    subject實現的介面是:cn.edu.ustc.dynamicproxy.Subject,   
      
    執行結果為:  
    before calling public abstract void ***.Subject.request()  
    From real subject.  
    after calling public abstract void ***.Subject.request()  

PS:這個結果的資訊非常重要,至少對我來說。因為我在動態代理犯暈的根源就在於將上面的subject.request()理解錯了,至少是被表面所迷惑,沒有發現這個subject和Proxy之間的聯絡,一度糾結於最後呼叫的這個request()是怎麼和invoke()聯絡上的,而invoke又是怎麼知道request存在的。其實上面的true和class $Proxy0就能解決很多的疑問,再加上下面將要說的$Proxy0的原始碼,完全可以解決動態代理的疑惑了。

從以上程式碼和結果可以看出,我們並沒有顯示的呼叫invoke()方法,但是這個方法確實執行了。下面就整個的過程進行分析一下:
從Client中的程式碼看,可以從newProxyInstance這個方法作為突破口,我們先來看一下Proxy類中newProxyInstance方法的原始碼:

    public static Object newProxyInstance(ClassLoader loader,  
            Class<?>[] interfaces,  
            InvocationHandler h)  
    throws IllegalArgumentException  
    {  
        if (h == null) {  
            throw new NullPointerException();  
        }  
      
        /* 
         * Look up or generate the designated proxy class. 
         */  
        Class cl = getProxyClass(loader, interfaces);  
      
        /* 
         * Invoke its constructor with the designated invocation handler. 
         */  
        try {  
               /* 
                * Proxy原始碼開始有這樣的定義: 
                * private final static Class[] constructorParams = { InvocationHandler.class }; 
                * cons即是形參為InvocationHandler型別的構造方法 
               */  
            Constructor cons = cl.getConstructor(constructorParams);  
            return (Object) cons.newInstance(new Object[] { h });  
        } catch (NoSuchMethodException e) {  
            throw new InternalError(e.toString());  
        } catch (IllegalAccessException e) {  
            throw new InternalError(e.toString());  
        } catch (InstantiationException e) {  
            throw new InternalError(e.toString());  
        } catch (InvocationTargetException e) {  
            throw new InternalError(e.toString());  
        }  
    }  

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下幾件事. 
(1)根據引數loader和interfaces呼叫方法 getProxyClass(loader, interfaces)建立代理類$Proxy0.$Proxy0類 實現了interfaces的介面,並繼承了Proxy類. 
(2)例項化$Proxy0並在構造方法中把DynamicSubject傳過去,接著$Proxy0呼叫父類Proxy的構造器,為h賦值,如下:
    class Proxy{  
        InvocationHandler h=null;  
        protected Proxy(InvocationHandler h) {  
            this.h = h;  
        }  
        ...  
    }  

來看一下這個繼承了Proxy的$Proxy0的原始碼:
    public final class $Proxy0 extends Proxy implements Subject {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
      
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
      
                m3 = Class.forName("***.RealSubject").getMethod("request",  
                        new Class[0]);  
      
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
      
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        } //static  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        public final void request() {  
            try {  
                super.h.invoke(this, m3, null);  
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
    }  

接著把得到的$Proxy0例項強制轉換成Subject,並將引用賦給subject。當執行subject.request()方法時,就呼叫了$Proxy0類中的request()方法,進而呼叫父類Proxy中的h的invoke()方法.即InvocationHandler.invoke()。 

PS:1、需要說明的一點是,Proxy類中getProxyClass方法返回的是Proxy的Class類。之所以說明,是因為我一開始犯了個低階錯誤,以為返回的是“被代理類的Class類”- -!推薦看一下getProxyClass的原始碼,很長=。= 
        2、從$Proxy0的原始碼可以看出,動態代理類不僅代理了顯示定義的介面中的方法,而且還代理了java的根類Object中的繼承而來的equals()、hashcode()、toString()這三個方法,並且僅此三個方法。 

Q:到現在為止,還有一個疑問,invoke方法中的第一個引數是Proxy的例項(準確說,最終用到的是$Proxy0的例項),但是有什麼用呢?或者說,程式內是怎樣顯示出作用的? 
A:就本人目前的水平看來,這個proxy引數並沒有什麼作用,在整個動態代理機制中,並沒有用到InvocationHandler中invoke方法的proxy引數。而傳入的這個引數實際是代理類的一個例項。我想可能是為了讓程式設計師在invoke方法中使用反射來獲取關於代理類的一些資訊吧。

相關推薦

java動態代理invoke方法是如何自動呼叫

轉載宣告:本文轉載至 zcc_0015的專欄 一、動態代理與靜態代理的區別。 (1)Proxy類的程式碼被固定下來,不會因為業務的逐漸龐大而龐大; (2)可以實現AOP程式設計,這是靜態代理無法實現的; (3)解耦,如果用在web業務下,可以實現資料層和業務層的分離。 (

java 動態代理 為什麼在debug 時會多次執行invoke 內部方法

java 動態代理 為什麼在debug 時會多次執行invoke 內部方法 最近被一個同事問道該問題,有些模糊了,前來驗證記錄下。 copy了一個網上例項進行驗證 package com.huilong.hrs.portal.study.amn.jdkpox; import

JDK動態代理關於InvocationHandlerinvoke()方法呼叫問題

Java中動態代理的實現,關鍵就是這兩個東西:Proxy、InvocationHandler,下面從InvocationHandler介面中的invoke方法入手,簡單說明一下Java如何實現動態代理的。         首先,invoke方法的完整形式如下:  Jav

SpringAOP:Proxy動態代理淺解析(代理對象必須實現接口)

ima throwable light public RR eth 對象 處理 span 小貼士:以下內容純屬個人觀點,如有不當請指出並諒解 import java.lang.reflect.Proxy; Proxy可以動態代理一個對象 寫一個代理工廠類ProxyFac

java代理,靜態代理動態代理以及spring aop代理方式,實現原理統一彙總 SpringAOP的兩種代理方式(Java動態代理和CGLIB代理

若代理類在程式執行前就已經存在,那麼這種代理方式被成為 靜態代理 ,這種情況下的代理類通常都是我們在Java程式碼中定義的。 通常情況下, 靜態代理中的代理類和委託類會實現同一介面或是派生自相同的父類。 一、概述1. 什麼是代理我們大家都知道微商代理,簡單地說就是代替廠家賣商品,廠家“委託”代理為

Java動態代理使用錯誤引起的bean自動注入失敗

前言 前兩天碰到了一個問題,排查了好久,特此記錄下來,以供自勉,也分享給讀者。 1. 問題描述 本來跑的好好的程式碼,一次重新發布的時候突然失敗了,控制檯丟擲瞭如下異常資訊: org.springframework.beans.factory.BeanCreationExceptio

Java動態代理的兩種實現方法

AOP的攔截功能是由java中的動態代理來實現的。說白了,就是在目標類的基礎上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函式執行之前,或者目標類函式執行之後,或者在目標類函式丟擲異常時候執行。不同的切入時機對應不同的Interceptor的種類,如BeforeAd

利用Java 動態代理,自定義註解 讀取配置檔案的屬性值

Java動態代理在一些中介軟體中經常用到,或者一些大型專案中都會用到。 這裡順帶使用一下自定義註解方式,基於java 反射機制讀取.properties格式檔案。 demo的大致內容包含以下: 1.配置檔案:config.properties url=http://www.

系統性能監控系列1:使用JAVA動態代理實現非侵入式的效能測量方法

歡迎關注公眾號: 當我們開發的服務上線後,線上的系統執行狀態(是否正常,效能是否滿足需求)等等就成了架構師和研發工程師關心的問題 。對於系統監控有很多維度,比如:監控CPU,磁碟IO,監控服務請求的響應時間等。相對於這些來說,我今天要給大家分享的是具體的程式碼層次的

Java 動態代理原理及其在mybatis的應用

代理是一種基本的設計模式,代理模式的主要作用是為其他物件(被代理的物件,下面稱為原物件)提供一種代理以控制對這個物件的訪問。在某些情況下,一個物件不想或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。代理物件既可以將客戶端的請求完全

java動態代理——欄位和方法位元組碼的基礎結構及Proxy原始碼分析三

前文地址:https://www.cnblogs.com/tera/p/13280547.html 本系列文章主要是博主在學習spring aop的過程中瞭解到其使用了java動態代理,本著究根問底的態度,於是對java動態代理的本質原理做了一些研究,於是便有了這個系列的文章   接上文,我們對cl

java動態代理——代理方法的假設和驗證及Proxy原始碼分析五

前文地址 https://www.cnblogs.com/tera/p/13419025.html 本系列文章主要是博主在學習spring aop的過程中瞭解到其使用了java動態代理,本著究根問底的態度,於是對java動態代理的本質原理做了一些研究,於是便有了這個系列的文章 這個系列的文章的初衷是為了研究j

Java動態代理——框架的應用場景和基本原理

## **前言** 之前已經用了5篇文章完整解釋了java動態代理的原理,本文將會為這個系列補上最後一塊拼圖,展示java動態代理的使用方式和應用場景 主要分為以下4個部分 **1.為什麼要使用java動態代理** **2.如何使用java動態代理** **3.框架中java動態代理的應用** *

java動態代理【一】

bject print reat 映射 tor ted borde 需要 static java動態代理的定義:為其他目標類的方法增加切面的邏輯,即在執行目標類方法的時候,先去執行一段如校驗檢測的邏輯代碼。java通俗一點就是生成一個繼承目標類的子類,並在每個調用方法都添加

Java——動態代理

插入 ref exc tcl try 反射 mil 動態 () 在靜態代理中,我們在調用target類的時候,都是先拿到proxy類。由於proxy類中將target類作為了成員變量,而且跟target類繼承了一樣的接口,具有同樣的

Java動態代理

throws ice handle main throw int 表示 isp logs 首先我們定義一個接口 public interface SayService { public void say(); } View Code 接著實現這個接口 p

動態代理的自定義註解樣式

interface pre 運行期 type import 生存 nbsp pub tar 動態代理中的自定義註解的樣式    @Target(ElementType.METHOD) 代表此註解使用對象是method@Retention(RetentionPolicy.RU

Java動態代理學習【Spring AOP基礎之一】

tor -1 我們 null exception 文件 cat static 一個   Spring AOP使用的其中一個底層技術就是Java的動態代理技術。Java的動態代理技術主要圍繞兩個類進行的    java.lang.reflect.InvocationHan

JAVA動態代理機制解析

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

Java動態代理 深度詳解

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