最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java JDK動態(tài)代理(AOP)的實現(xiàn)原理與使用詳析

 更新時間:2017年07月18日 10:04:25   作者:衣舞晨風  
所謂代理,就是一個人或者一個機構代表另一個人或者另一個機構采取行動。下面這篇文章主要給大家介紹了關于Java JDK動態(tài)代理(AOP)實現(xiàn)原理與使用的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。

本文主要給大家介紹了關于Java JDK動態(tài)代理(AOP)實現(xiàn)原理與使用的相關內(nèi)容,分享出來供大家參考學習,下面來一起看看詳細的介紹:

一、什么是代理?

代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委托類預處理消息,過濾消息并轉發(fā)消息,以及進行消息被委托類執(zhí)行后的后續(xù)處理。

代理模式UML圖:

簡單結構示意圖:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1、需要動態(tài)代理的接口:

package jiankunking; 
 
/** 
 * 需要動態(tài)代理的接口 
 */ 
public interface Subject 
{ 
 /** 
 * 你好 
 * 
 * @param name 
 * @return 
 */ 
 public String SayHello(String name); 
 
 /** 
 * 再見 
 * 
 * @return 
 */ 
 public String SayGoodBye(); 
} 

2、需要代理的實際對象

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

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

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

4、測試

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

5、輸出結果如下:

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

四、動態(tài)代理怎么實現(xiàn)的?

從使用代碼中可以看出,關鍵點在:

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

通過跟蹤提示代碼可以看出:當代理對象調(diào)用真實對象的方法時,其會自動的跳轉到代理對象關聯(lián)的handler對象的invoke方法來進行調(diào)用。

也就是說,當代碼執(zhí)行到:

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

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

以下代碼來自:JDK1.8.0_92

既然生成代理對象是用的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); 
 } 
 
 /* 
 * 獲得與指定類裝載器和一組接口相關的代理類類型對象 
 */ 
 Class<?> cl = getProxyClass0(loader, intfs); 
 
 /* 
 * 通過反射獲取構造函數(shù)對象并生成代理類實例 
 */ 
 try { 
 if (sm != null) { 
 checkNewProxyPermission(Reflection.getCallerClass(), cl); 
 } 
 //獲取代理對象的構造方法(也就是$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; 
  } 
 }); 
 } 
 //生成代理類的實例并把InvocationHandlerImpl的實例傳給它的構造方法 
 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); 
 } 
 } 

我們再進去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); 
 } 

 真相還是沒有來到,繼續(xù),看一下proxyClassCache

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

奧,原來用了一下緩存啊

那么它對應的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這個方法在key不存在的時候加入一個值,如果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(); 獲取動態(tài)代理類,其中supplier是Factory,這個類定義在WeakCach的內(nè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)重點還是木有出現(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()); 
 } 
 } 
 } 

通過看代碼終于找到了重點:

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

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

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

可以看一下這里代理對象的類型:

我們用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并實現(xiàn)了我們定義的Subject接口

也就是說:

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

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

public final String SayHello(String paramString) 

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

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

五、結論

到了這里,終于解答了:

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

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

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

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

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

  • 通過實現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器;
  • 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創(chuàng)建動態(tài)代理類;
  • 通過反射機制獲得動態(tài)代理類的構造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型;
  • 通過構造函數(shù)創(chuàng)建動態(tài)代理類實例,構造時調(diào)用處理器對象作為參數(shù)被傳入。

本文參考過:

http://m.fzitv.net/kf/201608/533663.html

http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

好了,以上就是這篇文章的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關文章

  • Mybatis中返回主鍵值方式

    Mybatis中返回主鍵值方式

    這篇文章主要介紹了Mybatis中返回主鍵值方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java實用工具之StringJoiner詳解

    Java實用工具之StringJoiner詳解

    這篇文章主要介紹了Java實用工具之StringJoiner詳解,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-05-05
  • JavaWeb購物車項目開發(fā)實戰(zhàn)指南

    JavaWeb購物車項目開發(fā)實戰(zhàn)指南

    之前沒有接觸過購物車的東東,也不知道購物車應該怎么做,所以在查詢了很多資料,總結一下購物車的功能實現(xiàn),下面這篇文章主要給大家介紹了關于JavaWeb購物車項目開發(fā)的相關資料,需要的朋友可以參考下
    2022-06-06
  • mybatis分頁效果實現(xiàn)代碼

    mybatis分頁效果實現(xiàn)代碼

    這篇文章主要為大家詳細介紹了mybatis分頁效果的實現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Java讀取傳輸FTP文件實現(xiàn)示例

    Java讀取傳輸FTP文件實現(xiàn)示例

    本文主要介紹了Java讀取傳輸FTP文件方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • Java實現(xiàn)洗牌發(fā)牌的方法

    Java實現(xiàn)洗牌發(fā)牌的方法

    這篇文章主要介紹了Java實現(xiàn)洗牌發(fā)牌的方法,涉及java針對數(shù)組的遍歷與排序操作相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • Java調(diào)用構造函數(shù)和方法及使用詳解

    Java調(diào)用構造函數(shù)和方法及使用詳解

    在Java編程中,構造函數(shù)用于初始化新創(chuàng)建的對象,而方法則用于執(zhí)行對象的行為,構造函數(shù)在使用new關鍵字創(chuàng)建類實例時自動調(diào)用,沒有返回類型,并且名稱與類名相同,本文通過示例詳細介紹了如何在Java中使用構造函數(shù)和方法,感興趣的朋友一起看看吧
    2024-10-10
  • Java模擬計算機的整數(shù)乘積計算功能示例

    Java模擬計算機的整數(shù)乘積計算功能示例

    這篇文章主要介紹了Java模擬計算機的整數(shù)乘積計算功能,簡單分析了計算機數(shù)值進制轉換與通過位移進行乘積計算的原理,并結合具體實例給出了java模擬計算機成績運算的相關操作技巧,需要的朋友可以參考下
    2017-09-09
  • Java如何生成隨機數(shù)不了解下嗎

    Java如何生成隨機數(shù)不了解下嗎

    我們在學習 Java 基礎時就知道可以生成隨機數(shù),可以為我們枯燥的學習增加那么一丟丟的樂趣,本文就來和大家介紹Java生成隨機數(shù)的常用方法,需要的可以參考下
    2023-08-08
  • SpringBoot使用easy-captcha 實現(xiàn)驗證碼登錄功能(解決思路)

    SpringBoot使用easy-captcha 實現(xiàn)驗證碼登錄功能(解決思路)

    文章介紹了如何使用Spring Boot和Easy-Captcha實現(xiàn)驗證碼登錄功能,后端通過Easy-Captcha生成驗證碼并存儲在Redis中,前端獲取驗證碼并顯示給用戶,登錄時,前端將用戶輸入的驗證碼和標識符發(fā)送到后端進行驗證,感興趣的朋友跟隨小編一起看看吧
    2025-02-02

最新評論

富裕县| 海安县| 金乡县| 惠东县| 健康| 新龙县| 宣武区| 巨野县| 云安县| 丹凤县| 曲麻莱县| 安达市| 凤阳县| 金坛市| 哈尔滨市| 汝州市| 嫩江县| 察哈| 奇台县| 景洪市| 康马县| 兰考县| 新平| 新津县| 永新县| 奉贤区| 洛浦县| 织金县| 栾川县| 新源县| 柳林县| 德江县| 宁河县| 临武县| 邯郸县| 梅州市| 宜兰市| 青州市| 横山县| 齐齐哈尔市| 永康市|