溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么

發(fā)布時(shí)間:2020-11-26 17:21:11 來(lái)源:億速云 閱讀:138 作者:Leah 欄目:編程語(yǔ)言

在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

一、什么是代理?

代理是一種常用的設(shè)計(jì)模式,其目的就是為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)對(duì)象的訪問(wèn)。代理類負(fù)責(zé)為委托類預(yù)處理消息,過(guò)濾消息并轉(zhuǎn)發(fā)消息,以及進(jìn)行消息被委托類執(zhí)行后的后續(xù)處理。

代理模式UML圖:

在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么

簡(jiǎn)單結(jié)構(gòu)示意圖:

在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么

為了保持行為的一致性,代理類和委托類通常會(huì)實(shí)現(xiàn)相同的接口,所以在訪問(wèn)者看來(lái)兩者沒(méi)有絲毫的區(qū)別。通過(guò)代理類這中間一層,能有效控制對(duì)委托類對(duì)象的直接訪問(wèn),也可以很好地隱藏和保護(hù)委托類對(duì)象,同時(shí)也為實(shí)施不同控制策略預(yù)留了空間,從而在設(shè)計(jì)上獲得了更大的靈活性。Java 動(dòng)態(tài)代理機(jī)制以巧妙的方式近乎完美地實(shí)踐了代理模式的設(shè)計(jì)理念。

二、Java 動(dòng)態(tài)代理類

Java動(dòng)態(tài)代理類位于java.lang.reflect包下,一般主要涉及到以下兩個(gè)類:

(1)Interface InvocationHandler:該接口中僅定義了一個(gè)方法

publicobject invoke(Object obj,Method method, Object[] args)

在實(shí)際使用時(shí),第一個(gè)參數(shù)obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數(shù)數(shù)組。這個(gè)抽象方法在代理類中動(dòng)態(tài)實(shí)現(xiàn)。

(2)Proxy:該類即為動(dòng)態(tài)代理類,其中主要包含以下內(nèi)容:

protected Proxy(InvocationHandler h) :構(gòu)造函數(shù),用于給內(nèi)部的h賦值。

static Class getProxyClass (ClassLoaderloader, Class[] interfaces) :獲得一個(gè)代理類,其中l(wèi)oader是類裝載器,interfaces是真實(shí)類所擁有的全部接口的數(shù)組。

static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) :返回代理類的一個(gè)實(shí)例,返回后的代理類可以當(dāng)作被代理類使用(可使用被代理類的在Subject接口中聲明過(guò)的方法)

所謂DynamicProxy是這樣一種class:它是在運(yùn)行時(shí)生成的class,在生成它時(shí)你必須提供一組interface給它,然后該class就宣稱它實(shí)現(xiàn)了這些 interface。你當(dāng)然可以把該class的實(shí)例當(dāng)作這些interface中的任何一個(gè)來(lái)用。當(dāng)然,這個(gè)DynamicProxy其實(shí)就是一個(gè)Proxy,它不會(huì)替你作實(shí)質(zhì)性的工作,在生成它的實(shí)例時(shí)你必須提供一個(gè)handler,由它接管實(shí)際的工作。

在使用動(dòng)態(tài)代理類時(shí),我們必須實(shí)現(xiàn)InvocationHandler接口

通過(guò)這種方式,被代理的對(duì)象(RealSubject)可以在運(yùn)行時(shí)動(dòng)態(tài)改變,需要控制的接口(Subject接口)可以在運(yùn)行時(shí)改變,控制的方式(DynamicSubject類)也可以動(dòng)態(tài)改變,從而實(shí)現(xiàn)了非常靈活的動(dòng)態(tài)代理關(guān)系。

動(dòng)態(tài)代理步驟:

       1.創(chuàng)建一個(gè)實(shí)現(xiàn)接口InvocationHandler的類,它必須實(shí)現(xiàn)invoke方法

       2.創(chuàng)建被代理的類以及接口

       3.通過(guò)Proxy的靜態(tài)方法

           newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)創(chuàng)建一個(gè)代理

       4.通過(guò)代理調(diào)用方法

三、JDK的動(dòng)態(tài)代理怎么使用?

1、需要?jiǎng)討B(tài)代理的接口:

package jiankunking; 
 
/** 
 * 需要?jiǎng)討B(tài)代理的接口 
 */ 
public interface Subject 
{ 
 /** 
 * 你好 
 * 
 * @param name 
 * @return 
 */ 
 public String SayHello(String name); 
 
 /** 
 * 再見(jiàn) 
 * 
 * @return 
 */ 
 public String SayGoodBye(); 
}

2、需要代理的實(shí)際對(duì)象

package jiankunking; 
 
/** 
 * 實(shí)際對(duì)象 
 */ 
public class RealSubject implements Subject 
{ 
 
 /** 
 * 你好 
 * 
 * @param name 
 * @return 
 */ 
 public String SayHello(String name) 
 { 
 return "hello " + name; 
 } 
 
 /** 
 * 再見(jiàn) 
 * 
 * @return 
 */ 
 public String SayGoodBye() 
 { 
 return " good bye "; 
 } 
}

3、調(diào)用處理器實(shí)現(xiàn)類(有木有感覺(jué)這里就是傳說(shuō)中的AOP?。?/strong>

package jiankunking; 
 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
 
 
/** 
 * 調(diào)用處理器實(shí)現(xiàn)類 
 * 每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都需要指定一個(gè)實(shí)現(xiàn)了該接口的調(diào)用處理器對(duì)象 
 */ 
public class InvocationHandlerImpl implements InvocationHandler 
{ 
 
 /** 
 * 這個(gè)就是我們要代理的真實(shí)對(duì)象 
 */ 
 private Object subject; 
 
 /** 
 * 構(gòu)造方法,給我們要代理的真實(shí)對(duì)象賦初值 
 * 
 * @param subject 
 */ 
 public InvocationHandlerImpl(Object subject) 
 { 
 this.subject = subject; 
 } 
 
 /** 
 * 該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類上的所有方法調(diào)用。 
 * 調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上反射執(zhí)行 
 * 
 * @param proxy 代理類實(shí)例 
 * @param method 被調(diào)用的方法對(duì)象 
 * @param args 調(diào)用參數(shù) 
 * @return 
 * @throws Throwable 
 */ 
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
 { 
 //在代理真實(shí)對(duì)象前我們可以添加一些自己的操作 
 System.out.println("在調(diào)用之前,我要干點(diǎn)啥呢?"); 
 
 System.out.println("Method:" + method); 
 
 //當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用 
 Object returnValue = method.invoke(subject, args); 
 
 //在代理真實(shí)對(duì)象后我們也可以添加一些自己的操作 
 System.out.println("在調(diào)用之后,我要干點(diǎn)啥呢?"); 
 
 return returnValue; 
 } 
}

4、測(cè)試

package jiankunking; 
 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Proxy; 
 
/** 
 * 動(dòng)態(tài)代理演示 
 */ 
public class DynamicProxyDemonstration 
{ 
 public static void main(String[] args) 
 { 
 //代理的真實(shí)對(duì)象 
 Subject realSubject = new RealSubject(); 
 
 /** 
 * InvocationHandlerImpl 實(shí)現(xiàn)了 InvocationHandler 接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā) 
 * 其內(nèi)部通常包含指向委托類實(shí)例的引用,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過(guò)來(lái)的方法調(diào)用. 
 * 即:要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過(guò)該真實(shí)對(duì)象來(lái)調(diào)用其方法 
 */ 
 InvocationHandler handler = new InvocationHandlerImpl(realSubject); 
 
 ClassLoader loader = realSubject.getClass().getClassLoader(); 
 Class[] interfaces = realSubject.getClass().getInterfaces(); 
 /** 
 * 該方法用于為指定類裝載器、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例 
 */ 
 Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler); 
 
 System.out.println("動(dòng)態(tài)代理對(duì)象的類型:"+subject.getClass().getName()); 
 
 String hello = subject.SayHello("jiankunking"); 
 System.out.println(hello); 
// String goodbye = subject.SayGoodBye(); 
// System.out.println(goodbye); 
 } 
 
}

5、輸出結(jié)果如下:

演示demo下載地址:http://xiazai.jb51.net/201707/yuanma/DynamicProxyDemo(jb51.net).rar

四、動(dòng)態(tài)代理怎么實(shí)現(xiàn)的?

從使用代碼中可以看出,關(guān)鍵點(diǎn)在:

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

通過(guò)跟蹤提示代碼可以看出:當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用。

也就是說(shuō),當(dāng)代碼執(zhí)行到:

subject.SayHello("jiankunking")這句話時(shí),會(huì)自動(dòng)調(diào)用InvocationHandlerImpl的invoke方法。這是為啥呢?

=======橫線之間的是代碼跟分析的過(guò)程,不想看的朋友可以直接看結(jié)論============

以下代碼來(lái)自:JDK1.8.0_92

既然生成代理對(duì)象是用的Proxy類的靜態(tài)方newProxyInstance,那么我們就去它的源碼里看一下它到底都做了些什么?

/** 
 * Returns an instance of a proxy class for the specified interfaces 
 * that dispatches method invocations to the specified invocation 
 * handler. 
 * 
 * <p>{@code Proxy.newProxyInstance} throws 
 * {@code IllegalArgumentException} for the same reasons that 
 * {@code Proxy.getProxyClass} does. 
 * 
 * @param loader the class loader to define the proxy class 
 * @param interfaces the list of interfaces for the proxy class 
 * to implement 
 * @param h the invocation handler to dispatch method invocations to 
 * @return a proxy instance with the specified invocation handler of a 
 * proxy class that is defined by the specified class loader 
 * and that implements the specified interfaces 
 * @throws IllegalArgumentException if any of the restrictions on the 
 * parameters that may be passed to {@code getProxyClass} 
 * are violated 
 * @throws SecurityException if a security manager, <em>s</em>, is present 
 * and any of the following conditions is met: 
 * <ul> 
 * <li> the given {@code loader} is {@code null} and 
 * the caller's class loader is not {@code null} and the 
 * invocation of {@link SecurityManager#checkPermission 
 * s.checkPermission} with 
 * {@code RuntimePermission("getClassLoader")} permission 
 * denies access;</li> 
 * <li> for each proxy interface, {@code intf}, 
 * the caller's class loader is not the same as or an 
 * ancestor of the class loader for {@code intf} and 
 * invocation of {@link SecurityManager#checkPackageAccess 
 * s.checkPackageAccess()} denies access to {@code intf};</li> 
 * <li> any of the given proxy interfaces is non-public and the 
 * caller class is not in the same {@linkplain Package runtime package} 
 * as the non-public interface and the invocation of 
 * {@link SecurityManager#checkPermission s.checkPermission} with 
 * {@code ReflectPermission("newProxyInPackage.{package name}")} 
 * permission denies access.</li> 
 * </ul> 
 * @throws NullPointerException if the {@code interfaces} array 
 * argument or any of its elements are {@code null}, or 
 * if the invocation handler, {@code h}, is 
 * {@code null} 
 */ 
@CallerSensitive 
public static Object newProxyInstance(ClassLoader loader, 
   Class<?>[] interfaces, 
   InvocationHandler h) 
 throws IllegalArgumentException 
 { 
 //檢查h 不為空,否則拋異常 
 Objects.requireNonNull(h); 
 
 final Class<?>[] intfs = interfaces.clone(); 
 final SecurityManager sm = System.getSecurityManager(); 
 if (sm != null) { 
 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 
 } 
 
 /* 
 * 獲得與指定類裝載器和一組接口相關(guān)的代理類類型對(duì)象 
 */ 
 Class<?> cl = getProxyClass0(loader, intfs); 
 
 /* 
 * 通過(guò)反射獲取構(gòu)造函數(shù)對(duì)象并生成代理類實(shí)例 
 */ 
 try { 
 if (sm != null) { 
 checkNewProxyPermission(Reflection.getCallerClass(), cl); 
 } 
 //獲取代理對(duì)象的構(gòu)造方法(也就是$Proxy0(InvocationHandler h)) 
 final Constructor<?> cons = cl.getConstructor(constructorParams); 
 final InvocationHandler ih = h; 
 if (!Modifier.isPublic(cl.getModifiers())) { 
 AccessController.doPrivileged(new PrivilegedAction<Void>() { 
  public Void run() { 
  cons.setAccessible(true); 
  return null; 
  } 
 }); 
 } 
 //生成代理類的實(shí)例并把InvocationHandlerImpl的實(shí)例傳給它的構(gòu)造方法 
 return cons.newInstance(new Object[]{h}); 
 } catch (IllegalAccessException|InstantiationException e) { 
 throw new InternalError(e.toString(), e); 
 } catch (InvocationTargetException e) { 
 Throwable t = e.getCause(); 
 if (t instanceof RuntimeException) { 
 throw (RuntimeException) t; 
 } else { 
 throw new InternalError(t.toString(), t); 
 } 
 } catch (NoSuchMethodException e) { 
 throw new InternalError(e.toString(), e); 
 } 
 }

我們?cè)龠M(jìn)去getProxyClass0方法看一下:

/** 
 * Generate a proxy class. Must call the checkProxyAccess method 
 * to perform permission checks before calling this. 
 */ 
 private static Class<?> getProxyClass0(ClassLoader loader, 
   Class<?>... interfaces) { 
 if (interfaces.length > 65535) { 
 throw new IllegalArgumentException("interface limit exceeded"); 
 } 
 
 // If the proxy class defined by the given loader implementing 
 // the given interfaces exists, this will simply return the cached copy; 
 // otherwise, it will create the proxy class via the ProxyClassFactory 
 return proxyClassCache.get(loader, interfaces); 
 }

 真相還是沒(méi)有來(lái)到,繼續(xù),看一下proxyClassCache

/** 
 * a cache of proxy classes 
 */ 
 private static final WeakCache<ClassLoader, Class<?>[], Class<?>> 
 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

奧,原來(lái)用了一下緩存啊

那么它對(duì)應(yīng)的get方法啥樣呢?

/** 
 * Look-up the value through the cache. This always evaluates the 
 * {@code subKeyFactory} function and optionally evaluates 
 * {@code valueFactory} function if there is no entry in the cache for given 
 * pair of (key, subKey) or the entry has already been cleared. 
 * 
 * @param key possibly null key 
 * @param parameter parameter used together with key to create sub-key and 
 *  value (should not be null) 
 * @return the cached value (never null) 
 * @throws NullPointerException if {@code parameter} passed in or 
 *  {@code sub-key} calculated by 
 *  {@code subKeyFactory} or {@code value} 
 *  calculated by {@code valueFactory} is null. 
 */ 
 public V get(K key, P parameter) { 
 Objects.requireNonNull(parameter); 
 
 expungeStaleEntries(); 
 
 Object cacheKey = CacheKey.valueOf(key, refQueue); 
 
 // lazily install the 2nd level valuesMap for the particular cacheKey 
 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); 
 if (valuesMap == null) { 
 //putIfAbsent這個(gè)方法在key不存在的時(shí)候加入一個(gè)值,如果key存在就不放入 
 ConcurrentMap<Object, Supplier<V>> oldValuesMap 
 = map.putIfAbsent(cacheKey, 
   valuesMap = new ConcurrentHashMap<>()); 
 if (oldValuesMap != null) { 
 valuesMap = oldValuesMap; 
 } 
 } 
 
 // create subKey and retrieve the possible Supplier<V> stored by that 
 // subKey from valuesMap 
 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); 
 Supplier<V> supplier = valuesMap.get(subKey); 
 Factory factory = null; 
 
 while (true) { 
 if (supplier != null) { 
 // supplier might be a Factory or a CacheValue<V> instance 
 V value = supplier.get(); 
 if (value != null) { 
  return value; 
 } 
 } 
 // else no supplier in cache 
 // or a supplier that returned null (could be a cleared CacheValue 
 // or a Factory that wasn't successful in installing the CacheValue) 
 
 // lazily construct a Factory 
 if (factory == null) { 
 factory = new Factory(key, parameter, subKey, valuesMap); 
 } 
 
 if (supplier == null) { 
 supplier = valuesMap.putIfAbsent(subKey, factory); 
 if (supplier == null) { 
  // successfully installed Factory 
  supplier = factory; 
 } 
 // else retry with winning supplier 
 } else { 
 if (valuesMap.replace(subKey, supplier, factory)) { 
  // successfully replaced 
  // cleared CacheEntry / unsuccessful Factory 
  // with our Factory 
  supplier = factory; 
 } else { 
  // retry with current supplier 
  supplier = valuesMap.get(subKey); 
 } 
 } 
 } 
 }

我們可以看到它調(diào)用了 supplier.get(); 獲取動(dòng)態(tài)代理類,其中supplier是Factory,這個(gè)類定義在WeakCach的內(nèi)部。

來(lái)瞅瞅,get里面又做了什么?

public synchronized V get() { // serialize access 
 // re-check 
 Supplier<V> supplier = valuesMap.get(subKey); 
 if (supplier != this) { 
 // something changed while we were waiting: 
 // might be that we were replaced by a CacheValue 
 // or were removed because of failure -> 
 // return null to signal WeakCache.get() to retry 
 // the loop 
 return null; 
 } 
 // else still us (supplier == this) 
 
 // create new value 
 V value = null; 
 try { 
 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); 
 } finally { 
 if (value == null) { // remove us on failure 
  valuesMap.remove(subKey, this); 
 } 
 } 
 // the only path to reach here is with non-null value 
 assert value != null; 
 
 // wrap value with CacheValue (WeakReference) 
 CacheValue<V> cacheValue = new CacheValue<>(value); 
 
 // try replacing us with CacheValue (this should always succeed) 
 if (valuesMap.replace(subKey, this, cacheValue)) { 
 // put also in reverseMap 
 reverseMap.put(cacheValue, Boolean.TRUE); 
 } else { 
 throw new AssertionError("Should not reach here"); 
 } 
 
 // successfully replaced us with new CacheValue -> return the value 
 // wrapped by it 
 return value; 
 } 
 }

發(fā)現(xiàn)重點(diǎn)還是木有出現(xiàn),但我們可以看到它調(diào)用了valueFactory.apply(key, parameter)方法:

/** 
 * A factory function that generates, defines and returns the proxy class given 
 * the ClassLoader and array of interfaces. 
 */ 
 private static final class ProxyClassFactory 
 implements BiFunction<ClassLoader, Class<?>[], Class<?>> 
 { 
 // prefix for all proxy class names 
 private static final String proxyClassNamePrefix = "$Proxy"; 
 
 // next number to use for generation of unique proxy class names 
 private static final AtomicLong nextUniqueNumber = new AtomicLong(); 
 
 @Override 
 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { 
 
 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); 
 for (Class<?> intf : interfaces) { 
 /* 
 * Verify that the class loader resolves the name of this 
 * interface to the same Class object. 
 */ 
 Class<?> interfaceClass = null; 
 try { 
  interfaceClass = Class.forName(intf.getName(), false, loader); 
 } catch (ClassNotFoundException e) { 
 } 
 if (interfaceClass != intf) { 
  throw new IllegalArgumentException( 
  intf + " is not visible from class loader"); 
 } 
 /* 
 * Verify that the Class object actually represents an 
 * interface. 
 */ 
 if (!interfaceClass.isInterface()) { 
  throw new IllegalArgumentException( 
  interfaceClass.getName() + " is not an interface"); 
 } 
 /* 
 * Verify that this interface is not a duplicate. 
 */ 
 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; 
 
 /* 
 * Record the package of a non-public proxy interface so that the 
 * proxy class will be defined in the same package. Verify that 
 * all non-public proxy interfaces are in the same package. 
 */ 
 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"); 
  } 
 } 
 } 
 
 if (proxyPkg == null) { 
 // if no non-public proxy interfaces, use com.sun.proxy package 
 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 
 } 
 
 /* 
 * Choose a name for the proxy class to generate. 
 */ 
 long num = nextUniqueNumber.getAndIncrement(); 
 String proxyName = proxyPkg + proxyClassNamePrefix + num; 
 
 /* 
 * Generate the specified proxy class. 
 */ 
 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( 
 proxyName, interfaces, accessFlags); 
 try { 
 return defineClass0(loader, proxyName, 
   proxyClassFile, 0, proxyClassFile.length); 
 } catch (ClassFormatError e) { 
 /* 
 * A ClassFormatError here means that (barring bugs in the 
 * proxy class generation code) there was some other 
 * invalid aspect of the arguments supplied to the proxy 
 * class creation (such as virtual machine limitations 
 * exceeded). 
 */ 
 throw new IllegalArgumentException(e.toString()); 
 } 
 } 
 }

通過(guò)看代碼終于找到了重點(diǎn):

//生成字節(jié)碼 
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

那么接下來(lái)我們也使用測(cè)試一下,使用這個(gè)方法生成的字節(jié)碼是個(gè)什么樣子:

package jiankunking; 
 
import sun.misc.ProxyGenerator; 
 
import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Proxy; 
 
/** 
 * 動(dòng)態(tài)代理演示 
 */ 
public class DynamicProxyDemonstration 
{ 
 public static void main(String[] args) 
 { 
 //代理的真實(shí)對(duì)象 
 Subject realSubject = new RealSubject(); 
 
 /** 
 * InvocationHandlerImpl 實(shí)現(xiàn)了 InvocationHandler 接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā) 
 * 其內(nèi)部通常包含指向委托類實(shí)例的引用,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過(guò)來(lái)的方法調(diào)用. 
 * 即:要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過(guò)該真實(shí)對(duì)象來(lái)調(diào)用其方法 
 */ 
 InvocationHandler handler = new InvocationHandlerImpl(realSubject); 
 
 ClassLoader loader = handler.getClass().getClassLoader(); 
 Class[] interfaces = realSubject.getClass().getInterfaces(); 
 /** 
 * 該方法用于為指定類裝載器、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例 
 */ 
 Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler); 
 
 System.out.println("動(dòng)態(tài)代理對(duì)象的類型:"+subject.getClass().getName()); 
 
 String hello = subject.SayHello("jiankunking"); 
 System.out.println(hello); 
 // 將生成的字節(jié)碼保存到本地, 
 createProxyClassFile(); 
 } 
 private static void createProxyClassFile(){ 
 String name = "ProxySubject"; 
 byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class}); 
 FileOutputStream out =null; 
 try { 
 out = new FileOutputStream(name+".class"); 
 System.out.println((new File("hello")).getAbsolutePath()); 
 out.write(data); 
 } catch (FileNotFoundException e) { 
 e.printStackTrace(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 }finally { 
 if(null!=out) try { 
 out.close(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } 
 } 
 } 
 
}

可以看一下這里代理對(duì)象的類型:

在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么

我們用jd-jui 工具將生成的字節(jié)碼反編譯:

import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 
import java.lang.reflect.UndeclaredThrowableException; 
import jiankunking.Subject; 
 
public final class ProxySubject 
 extends Proxy 
 implements Subject 
{ 
 private static Method m1; 
 private static Method m3; 
 private static Method m4; 
 private static Method m2; 
 private static Method m0; 
 
 public ProxySubject(InvocationHandler paramInvocationHandler) 
 { 
 super(paramInvocationHandler); 
 } 
 
 public final boolean equals(Object paramObject) 
 { 
 try 
 { 
 return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); 
 } 
 catch (Error|RuntimeException localError) 
 { 
 throw localError; 
 } 
 catch (Throwable localThrowable) 
 { 
 throw new UndeclaredThrowableException(localThrowable); 
 } 
 } 
 
 public final String SayGoodBye() 
 { 
 try 
 { 
 return (String)this.h.invoke(this, m3, null); 
 } 
 catch (Error|RuntimeException localError) 
 { 
 throw localError; 
 } 
 catch (Throwable localThrowable) 
 { 
 throw new UndeclaredThrowableException(localThrowable); 
 } 
 } 
 
 public final String SayHello(String paramString) 
 { 
 try 
 { 
 return (String)this.h.invoke(this, m4, new Object[] { paramString }); 
 } 
 catch (Error|RuntimeException localError) 
 { 
 throw localError; 
 } 
 catch (Throwable localThrowable) 
 { 
 throw new UndeclaredThrowableException(localThrowable); 
 } 
 } 
 
 public final String toString() 
 { 
 try 
 { 
 return (String)this.h.invoke(this, m2, null); 
 } 
 catch (Error|RuntimeException localError) 
 { 
 throw localError; 
 } 
 catch (Throwable localThrowable) 
 { 
 throw new UndeclaredThrowableException(localThrowable); 
 } 
 } 
 
 public final int hashCode() 
 { 
 try 
 { 
 return ((Integer)this.h.invoke(this, m0, null)).intValue(); 
 } 
 catch (Error|RuntimeException localError) 
 { 
 throw localError; 
 } 
 catch (Throwable localThrowable) 
 { 
 throw new UndeclaredThrowableException(localThrowable); 
 } 
 } 
 
 static 
 { 
 try 
 { 
 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); 
 m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]); 
 m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") }); 
 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 
 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 
 return; 
 } 
 catch (NoSuchMethodException localNoSuchMethodException) 
 { 
 throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); 
 } 
 catch (ClassNotFoundException localClassNotFoundException) 
 { 
 throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); 
 } 
 } 
}

這就是最終真正的代理類,它繼承自Proxy并實(shí)現(xiàn)了我們定義的Subject接口

也就是說(shuō):

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

這里的subject實(shí)際是這個(gè)類的一個(gè)實(shí)例,那么我們調(diào)用它的:

public final String SayHello(String paramString)

就是調(diào)用我們定義的InvocationHandlerImpl的 invoke方法:

在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么

=======橫線之間的是代碼跟分析的過(guò)程,不想看的朋友可以直接看結(jié)論================

五、結(jié)論

到了這里,終于解答了:

subject.SayHello("jiankunking")這句話時(shí),為什么會(huì)自動(dòng)調(diào)用InvocationHandlerImpl的invoke方法?

因?yàn)镴DK生成的最終真正的代理類,它繼承自Proxy并實(shí)現(xiàn)了我們定義的Subject接口,在實(shí)現(xiàn)Subject接口方法的內(nèi)部,通過(guò)反射調(diào)用了InvocationHandlerImpl的invoke方法。

包含生成本地class文件的demo:

http://xiazai.jb51.net/201707/yuanma/DynamicProxyDemo2(jb51.net).rar

通過(guò)分析代碼可以看出Java 動(dòng)態(tài)代理,具體有如下四步驟:

  • 通過(guò)實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器;

  • 通過(guò)為 Proxy 類指定 ClassLoader 對(duì)象和一組 interface 來(lái)創(chuàng)建動(dòng)態(tài)代理類;

  • 通過(guò)反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型;

  • 通過(guò)構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)被傳入。

看完上述內(nèi)容,你們掌握在Java中實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI