1. 程式人生 > >JDK動態代理實現原理----JDK1.8

JDK動態代理實現原理----JDK1.8

1.代理模式

Proxy裡面有個RealSubject物件,初始化的時候將RealSubject物件傳入,進行Proxy中RealSubject的物件進行初始化,然後Proxy中的方法,都是呼叫Proxy物件的方法,可在前後加需求,客戶端只需使用多型,構造一個介面,即可。呼叫介面的方法,實際上就是呼叫Proxy方法,即RealSubject方法。

2.靜態代理

按上述類圖進行操作就是靜態代理,靜態代理如果要對多個方法進行處理,就得在多個方法前後進行修改,不方便,因此產生動態代理。

3.動態代理

代理類在程式執行時建立的代理方式,可以方便的對代理類的函式進行統一的處理,而不用修改代理類的每個方法。

3.1整體的用法

介面

具體物件

代理類

測試類

3.2底層原理

主要關於InvocationHandler和Proxy,前面看不懂沒事,堅持看完你就懂了,我也是這麼過來的

InvocationHandler介面

子類重寫的時候,需要將具體實現類物件(不是代理類)傳遞進去,然後用反射呼叫具體實現類的方法,同時增加其他業務邏輯。

Proxy類

publicstatic Object newProxyInstance(ClassLoader loader,

                                          Class<?>[]

interfaces,

                                          InvocationHandler h)

throws IllegalArgumentException

    {

        Objects.requireNonNull(h);handler的非空判斷

final Class<?>[] intfs = interfaces.clone();

final SecurityManager sm = System.getSecurityManager();

if

(sm != null) {

            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

        }

        Class<?> cl = getProxyClass0(loader, intfs);根據classloader和介面陣列生成代理類的class物件

try {

if (sm != null) {

                checkNewProxyPermission(Reflection.getCallerClass(), cl);

            }

獲取動態生成的代理類中引數為InvocationHandler的構造方法

//private static final Class<?>[] constructorParams = { InvocationHandler.class };

final Constructor<?> cons = cl.getConstructor(constructorParams);

final InvocationHandler ih = h;

得到cl代理類class中類的修飾符,然後判斷是不是public型別,如果是,就返回指定構造器cons的例項,即代理物件的例項,如果不是,就將cons構造方法設定為可訪問。

if (!Modifier.isPublic(cl.getModifiers())) {

                AccessController.doPrivileged(new PrivilegedAction<Void>() {

public Void run() {

cons.setAccessible(true);

returnnull;

                    }

                });

            }

returncons.newInstance(new Object[]{h});

        } catch (IllegalAccessException|InstantiationException e) {

thrownew InternalError(e.toString(), e);

        } catch (InvocationTargetException e) {

            Throwable t = e.getCause();

if (tinstanceof RuntimeException) {

throw (RuntimeException) t;

            } else {

thrownew InternalError(t.toString(), t);

            }

        } catch (NoSuchMethodException e) {

thrownew InternalError(e.toString(), e);

        }

    }

1、根據傳遞進來的ClassLoader,以及我們的代理物件的父介面陣列,來動態建立二進位制的class檔案,然後根據建立好的Class二進位制檔案,獲取到建立的動態代理類的Class物件。

2、通過代理類的class物件,獲取class物件中引數為InvocationHandler的構造方法

3、判斷構造方法的訪問修飾符,如果不是public的,將其設定成可以訪問的

4、呼叫構造器的newInstance方法,引數為InvocationHandler,返回代理類的例項

private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {

        if (interfaces.length > 65535) {

            throw new IllegalArgumentException("interface limit exceeded");

        }

proxyClassCacheWeakCache型別,如果快取中包含介面陣列和classloaderclass類已經存在,就返回快取副本,否則通過ProxyClassFactory建立一個對應的class物件

        return proxyClassCache.get(loader, interfaces);

    }

WeakCache這個物件當中會在get取不到值時,去生成一個值,放入進去;

ProxyClassFactoryProxy的一個靜態內部類,主要就是用來根據classLoader和介面陣列來生成Class物件的。

subKeyFactory.apply(key, parameter) 方法呼叫的是[email protected](ClassLoader loader, Class<?>[] interfaces) 方法

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);用來存傳入的介面

            for (Class<?> intf : interfaces) {

                Class<?> interfaceClass = null;

                try {

根據介面全限定類名和classLoader,獲取到介面class物件

                    interfaceClass = Class.forName(intf.getName(), false, loader);

                } catch (ClassNotFoundException e) {

                }

如果兩次介面class物件不一致,直接丟擲異常,說明建立錯誤

                if (interfaceClass != intf) {

                    throw new IllegalArgumentException(

                        intf + " is not visible from class loader");

                }

                判斷創建出來的介面是不是介面型別,不是就拋異常

                if (!interfaceClass.isInterface()) {

                    throw new IllegalArgumentException(

                        interfaceClass.getName() + " is not an interface");

                }

                判斷介面的set集合是否已經存在了該介面類,存在丟擲異常,不存在就新增進去

                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {

                    throw new IllegalArgumentException(

                        "repeated interface: " + interfaceClass.getName());

                }

            }

            String proxyPkg = null;     // package to define proxy class in

            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

          判斷非public介面是不是在同一個包內,如果不是,丟擲異常;

          宣告代理類所在的包位置,

            for (Class<?> intf : interfaces) {

                int flags = intf.getModifiers();

                if (!Modifier.isPublic(flags)) {

                    accessFlags = Modifier.FINAL;

                    String name = intf.getName();

                    int n = name.lastIndexOf('.');

                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));

                    if (proxyPkg == null) {

                        proxyPkg = pkg;

                    } else if (!pkg.equals(proxyPkg)) {

                        throw new IllegalArgumentException(

                            "non-public interfaces from different packages");

                    }

                }

            }

如果都是public介面設定全限定類名com.sun.proxy.$proxy0

            if (proxyPkg == null) {

                // if no non-public proxy interfaces, use com.sun.proxy package

                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";

            }

            long num = nextUniqueNumber.getAndIncrement();

            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            根據代理類全限定類名,介面陣列,訪問修飾符,生成代理類的位元組碼

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

                proxyName, interfaces, accessFlags);

            try {

根據生成的位元組碼,建立class物件並返回。Native方法

                return defineClass0(loader, proxyName,

                                    proxyClassFile, 0, proxyClassFile.length);

            } catch (ClassFormatError e) {

                throw new IllegalArgumentException(e.toString());

            }

        }

    }

可以看到這段程式碼就是設定好需要生成的類的類名,然後呼叫ProxyGenerator.generateProxyClass來生成代理類的位元組碼

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {

        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);

        final byte[] var4 = var3.generateClassFile();

     //中間省略掉一部分程式碼

        return var4;

}

private byte[] generateClassFile() {

        //將object類當中的 hashcode,equals,toString方法新增到動態代理類當中

        this.addProxyMethod(hashCodeMethod, Object.class);

        this.addProxyMethod(equalsMethod, Object.class);

        this.addProxyMethod(toStringMethod, Object.class);

        Class[] var1 = this.interfaces;

        int var2 = var1.length;

        int var3;

        Class var4;

        //遍歷父介面資料

        for(var3 = 0; var3 < var2; ++var3) {

            var4 = var1[var3];

            //獲取每個介面當中的方法

            Method[] var5 = var4.getMethods();

            int var6 = var5.length;

            //遍歷介面當中的方法,將介面當中的方法都新增至動態代理類當中

            for(int var7 = 0; var7 < var6; ++var7) {

                Method var8 = var5[var7];

                this.addProxyMethod(var8, var4);

            }

        }

        Iterator var11 = this.proxyMethods.values().iterator();

        //檢查代理類當中的返回型別

        List var12;

        while(var11.hasNext()) {

            var12 = (List)var11.next();

            checkReturnTypes(var12);

        }

        Iterator var15;

        try {

            // 將構造方法新增至代理類當中的方法集合中

            this.methods.add(this.generateConstructor());

            var11 = this.proxyMethods.values().iterator();

            //遍歷代理類當中的方法,此處使用兩層迴圈,是因為方法簽名相同的,可能有多個方法

            while(var11.hasNext()) {

                var12 = (List)var11.next();

                var15 = var12.iterator();

                while(var15.hasNext()) {

                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();

                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));

                    this.methods.add(var16.generateMethod());

                }

            }

            // 將靜態程式碼塊新增進去

            this.methods.add(this.generateStaticInitializer());

        } catch (IOException var10) {

            throw new InternalError("unexpected I/O Exception", var10);

        }

        if(this.methods.size() > '\uffff') {

            throw new IllegalArgumentException("method limit exceeded");

        } else if(this.fields.size() > '\uffff') {

            throw new IllegalArgumentException("field limit exceeded");

        } else {

                /**

                 * 省略部分程式碼

                 **/

                return var13.toByteArray();

            } catch (IOException var9) {

                throw new InternalError("unexpected I/O Exception", var9);

            }

        }

    }

生成了代理類物件,繼承自Proxy,實現了subject介面,其構造方法是傳遞了InvocationHandler引數,同時實現了equals ,hashcode,toString方法,以及我們自己定義的request方法。

這裡就能清楚的看到,代理類中,request方法,其實就是呼叫InvocationHandler實現類中invoke方法,m3就是RealSubject中定義的request方法

這裡就能知道,呼叫Subject的request的時候,為什麼能夠呼叫動態代理類的invoke方法了。因為在呼叫bind中的Proxy.newProxyInstance的時候,傳入的是我們classLoader,代理類的父介面,和自定義的InvocationHandler,因此生成的代理類物件中的h就是自定義的InvocationHandler。

整體流程

使用方法

定義一個介面Subject,裡面有抽象方法request,一個介面的實現類RealSubject,裡面有真正的request方法,定義個ProxyHandler,實現InvocationHandler介面,有ProxyHandler中有個目標物件target,用於傳入RealSubject的例項;裡面有兩個方法,bind方法,將傳入的RealSubject物件,賦值給target,並呼叫Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this)返回代理類物件的例項;還有一個方法,invoke(Object proxy, Method method, Object[] args)方法,在method.invoke前後可以加邏輯功能;method方法物件包含方法的相關資訊,method.invoke就會去找target物件中,指定引數與該方法匹配的方法。

底層原理

首先分析代理物件的產生過程,最後分析代理物件的產生後的樣子

產生過程:

Proxy.newInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this) 方法,該方法主要做了

①獲取代理類的介面Subject.class物件的副本intfs。

②呼叫getProxyClass0(loader, intfs)方法,獲取代理類的class物件

--2.1 getProxyClass方法會去呼叫proxyClassCache.get(loader,interfaces)方法,該方法的作用,如果快取中已經有該介面對應的代理類的副本,那麼返回代理類的副本,否則使用ProxyClassFactory的apply方法建立一個class物件;get方法中有一句表現了這個過程,如下圖

SubkeyFactory與ProxyClassFactory都是BiFunction介面的的實現類,此處呼叫的是ProxyClassFactory的apply方法。

----2.1.1 apply方法:根據介面的全限定類名以及指定的classLoader,呼叫class.forName方法得到介面的class物件interfaceClass,進行判斷是否是介面型別,是否有重複,

----2.1.2 設定代理類所在的包路徑,如果有非public介面,且不是在同一個包內,拋異常,如果在同一個包內,就設定代理類的包路徑為proxyPkg,如果都是public介面,那麼生成的代理類的包路徑proxyPkg設定為com.sum.proxy.

----2.1.3 根據包路徑,類名字首和num數字,確定代理類的名字

----2.1.4 呼叫ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);內部生成一個ProxyGenerator物件,呼叫ProxyGenerator.generateClassFile()得到代理類的位元組碼檔案;

------2.1.4.1 ProxyGenerator.generateClassFile()方法,為代理類添加了三個object類的hashcode,equals,toString方法,遍歷父介面,獲取每個介面中的方法,新增方法,新增構造方法等,都加入到代理中;返回二進位制位元組碼檔案

----2.1.5 呼叫defineClass0(loader, proxyName, proxyClassFile, 0,proxyClassFile.length);將位元組碼檔案建立為class物件,返回

③獲取到代理類的class物件後,得到代理類引數為InvocationHandler的構造方法,如果是非public型別,就使用setAccessible設定為true,然後呼叫cons.newInstance(new Object[]{h})來生成代理類物件;

④在代理類物件中,有個request方法,方法中預設會呼叫Proxy類裡面的InvocationHandler也就是我們自定義的InvocationHandler的invoke方法。這就實現了動態代理

相關推薦

JDK動態代理實現原理----JDK1.8

1.代理模式 Proxy裡面有個RealSubject物件,初始化的時候將RealSubject物件傳入,進行Proxy中RealSubject的物件進行初始化,然後Proxy中的方法,都是呼叫Proxy物件的方法,可在前後加需求,客戶端只需使用多型,構造一個介面,即可

JDK動態代理實現原理

之前雖然會用JDK的動態代理,但是有些問題卻一直沒有搞明白。比如說:InvocationHandler的invoke方法是由誰來呼叫的,代理物件是怎麼生成的,直到前幾個星期才把這些問題全部搞明白了。      廢話不多說了,先來看一下JDK的動態是怎麼用的。  Java程式碼

Java中HashMap底層實現原理(JDK1.8)源碼分析

blank imp dash logs || 屬性 lte das ces 這幾天學習了HashMap的底層實現,但是發現好幾個版本的,代碼不一,而且看了Android包的HashMap和JDK中的HashMap的也不是一樣,原來他們沒有指定JDK版本,很多文章都是舊版本J

攔截器(由JDK動態代理實現的攔截器)

gpo ret throwable voc row 接口 his 截器 運行 要實現攔截器,首先我們需要定義幾個類和接口 package com.xiawei.reflect.interceptor; public interface JavaBenDao { publi

Java,JDK動態代理原理分析

1. 代理基本概念:   以下是代理概念的百度解釋:代理(百度百科)        總之一句話:三個元素,資料--->代理物件--->真實物件;複雜一點的可以理解為五個元素:輸入資料--->代理物件--->真實物件--->代理物件---

轉:jdk動態代理實現

原文連結: jdk動態代理 注:文章中用常用的流程實現 動態代理,流程邏輯比較清晰。文章後面對 “為什麼要使用介面” 原理分析還未細看。 jdk的動態代理為什麼用介面,內部是什麼原理呢?看了幾篇文章貌似都沒講的清楚明白,因此來解釋一下。 先通過一個簡單例子實現功能: 1 //介面

Cglib動態代理實現原理.md

分享圖片 loader tran hand origin 每次 鉤子 def 因此 1. Cglib庫介紹 CGLIB是一個強大的、高性能的代碼生成庫。它被廣泛使用在基於代理的AOP框架(例如Spring AOP和dynaop)提供方法攔截。Hibernate作為最流行的O

Java中HashMap底層實現原理(JDK1.8)原始碼分析

在JDK1.6,JDK1.7中,HashMap採用位桶+連結串列實現,即使用連結串列處理衝突,同一hash值的連結串列都儲存在一個連結串列裡。但是當位於一個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查詢的效率較低。而JDK1.8中,HashMap採用位桶+

(轉載)Java中HashMap底層實現原理(JDK1.8)原始碼分析

近期在看一些java底層的東西,看到一篇分析hashMap不錯的文章,跟大家分享一下。 在JDK1.6,JDK1.7中,HashMap採用位桶+連結串列實現,即使用連結串列處理衝突,同一hash值的連結串列都儲存在一個連結串列裡。但是當位於一個桶中的元素較多,即hash值

JDK 動態代理執行原理

JDK 動態代理執行原理 程式演示 原始碼講解 總結 這幾天有空研究了下JDk的動態代理,JDK的動態代理類都在java.l

cglib動態代理實現原理

throws InvocationTargetException { // Byte code: // 0: aload_2//load paramObject到oprand stack // 1: checkcast 152 cgproxy/MyClass$$En

Java動態代理實現原理淺析

程式碼編寫 介面編寫 public interface TargetInterface { void targetMethod(); } 實現類編寫 public class Target implements TargetInterface {

java 動態代理實現原理

上篇講了:java動態代理淺析  這篇講講其內部實現原理。 1、相關的類和介面 1.1 java.lang.reflect.Proxy 這是 Java 動態代理機制的主類,它提供了一組靜態方法來為一組介面動態地生成代理類及其物件。 Proxy 的靜態方法: // 方法

JDK動態代理實現

代理模式是設計模式中比較重要也比較常用的模式,靜態代理靠我們手工完成代理類,這是一項比較耗時和費力的工作,一旦被代理介面發生改變需要修改的地方也比較多。但是動態代理就不會產生這樣的後遺症。java動態代理的出現,只要簡單地指定一組介面及委託類物件,便能動態地獲得代理類。 我們來看一下相關的類和介面: 1.j

Spring 之 AOP 動態代理實現原理

pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XM

Java面試必問之Hashmap底層實現原理(JDK1.8)

# 1. 前言 上一篇從原始碼方面瞭解了JDK1.7中Hashmap的實現原理,可以看到其原始碼相對還是比較簡單的。本篇筆者和大家一起學習下JDK1.8下Hashmap的實現。JDK1.8中對Hashmap做了以下改動。 - 預設初始化容量=0 - 引入紅黑樹,優化資料結構 - 將連結串列頭插法改為尾插法

【乾貨】JDK動態代理實現原理以及如何手寫一個JDK動態代理

動態代理 代理模式是設計模式中非常重要的一種型別,而設計模式又是程式設計中非常重要的知識點,特別是在業務系統的重構中,更是有舉足輕重的地位。代理模式從型別上來說,可以分為靜態代理和動態代理兩種型別。 在解釋動態代理之前我們先理解一下靜態代理: 首先你要明白靜態代理的作用 我們有一

jdk動態代理與cglib程式碼實現--SpringAop底層原理

動態代理分為兩類:基於介面的代理和基於繼承的代理 兩類實現的代表是:JDK代理 與 CGlib代理 cglib實現動態代理: 1、定義目標物件: public class RealSubject { //目標物件RealSubject,cglib不

細說JDK動態代理實現原理

<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-e2445db1a8.css">

Java JDK 動態代理使用及實現原理分析

一、什麼是代理? 代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。 代理模式 UML 圖: 簡單結構示意圖: 為了保持行為的一致性,代