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<?>[] InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h);handler的非空判斷 final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if 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"); } proxyClassCache是WeakCache型別,如果快取中包含介面陣列和classloader的class類已經存在,就返回快取副本,否則通過ProxyClassFactory建立一個對應的class物件 return proxyClassCache.get(loader, interfaces); } |
WeakCache這個物件當中會在get取不到值時,去生成一個值,放入進去;
ProxyClassFactory是Proxy的一個靜態內部類,主要就是用來根據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 圖: 簡單結構示意圖: 為了保持行為的一致性,代