定義一個公共接口和公共方法,java
package proxy; public interface Work { void sayHello(); String getName(); }
建立一個被代理類,實現公共接口和方法;jvm
package proxy; public class People implements Work { @Override public void sayHello() { System.out.println("my name is A"); } @Override public String getName() { return "A"; } }
建立一個代理類,實現公共接口和方法,這樣它們就具備了相同的方法。 ide
代理類中,須要持有一個被代理類對象,能夠當作代理類的成員變量,在代理類的構造方法中傳入並賦值。函數
package proxy; public class StaticProxyPeople implements Work { People A = new People(); public StaticProxyPeople(People A) { this.A = A; } @Override public void sayHello() { System.out.println("代理方法開始:"); A.sayHello(); System.out.println("代理方法結束。"); } @Override public String getName() { String name = A.getName(); name = "代理處理過的name:" + name; return name; } }
此時代理類和被代理類擁有相同的方法,而後在代理類的方法中,能夠調用被代理類的方法,達到操做被代理類的目的。而在調用方法先後,又能夠定義本身的特殊邏輯。性能
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { People people = new People(); StaticProxyPeople staticProxyPeople = new StaticProxyPeople(people); staticProxyPeople.sayHello(); System.out.println(staticProxyPeople.getName()); } } 輸出結果: 代理方法開始: my name is A 代理方法結束。 代理處理過的name:A
這樣子當外界但願調用被代理類的方法時,能夠建立一個代理類的對象,經過對代理類對象的方法的調用,達到操做被代理類對象的目的。this
缺點:一個代理類,只能針對一個被代理類進行代理。spa
在java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler接口、另外一個則是Proxyle類,這一個類和接口是實現咱們動態代理所必須用到的。代理
一、InvocationHandlercode
舉例:對象
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WrokHandler implements InvocationHandler { private Object subject; // 這個就是咱們要代理的真實對象,也就是真正執行業務邏輯的類 public WrokHandler(Object subject) { this.subject = subject; } /** * proxy: 指代JDK動態生成的最終代理對象 * method: 指代的是咱們所要調用真實對象的某個方法的Method對象 * args: 指代的是調用真實對象某個方法時接受的參數 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("調用方法:" + method.getName()+ "以前"); // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 result = method.invoke(subject, args);// 須要指定被代理對象和傳入參數 System.out.println("返回值:" + result); System.out.println("調用方法:" + method.getName()+ "以後"); return result; } } package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class SleepHandler implements InvocationHandler { private Object subject; // 這個就是咱們要代理的真實對象,也就是真正執行業務邏輯的類 public SleepHandler(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("SleepHandler.invoke 調用方法:" + method.getName() + "以前"); // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 result = method.invoke(subject, args);// 須要指定被代理對象和傳入參數 System.out.println("SleepHandler.invoke 返回值:" + result); System.out.println("SleepHandler.invoke 調用方法:" + method.getName() + "以後"); return result; } }
解析:
每個代理類都必需要實現InvocationHandler這個接口,
同時,也要持有一個被代理類真實對象。
代理類實現了InvocationHandler接口後,會重寫invoke方法。
三個參數分別是:
proxy: 指代JDK動態生成的最終代理對象
method: 指代的是咱們所要調用真實對象的某個方法的Method對象
args: 指代的是調用真實對象某個方法時接受的參數
而在invoke方法中,咱們經過method.invoke(subject, args); 來調用真實被代理對象的方法。
簡單來講,就是當定義了以上WorkHandler後,對代理類對象方法的調用,都會由invoke方法來處理,纔會傳達到被代理對象。
2.Proxy
舉例:
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { // 被代理的對象 People A = new People(); // 建立代理對象,代理對象持有一個被代理對象 InvocationHandler handler = new WrokHandler(A); // 得到被代理類的,類加載器Classloader // 做用: // 負責將 Class 加載到 JVM 中 // 審查每一個類由誰加載(父優先的等級加載機制) // 將 Class 字節碼從新解析成 JVM 統一要求的對象格式 ClassLoader loader = A.getClass().getClassLoader(); // 得到被代理類 實現的全部接口 Class[] interfaces = A.getClass().getInterfaces(); // 須要指定類裝載器、一組接口及調用處理器生成動態代理類實例 Work proxy = (Work) Proxy.newProxyInstance(loader, interfaces, handler); System.out.println("動態代理對象的類型:" + proxy.getClass().getName()); proxy.sayHello(); System.out.print(proxy.getName()); InvocationHandler handler1 = new SleepHandler(A); Sleep proxy1 = (Sleep) Proxy.newProxyInstance(loader, interfaces, handler1); System.out.println("動態代理對象的類型:" + proxy.getClass().getName()); proxy1.goToSleep(); proxy1.getUp(); } } 輸出結果: 動態代理對象的類型:$Proxy0 調用方法:sayHello以前 my name is A 返回值:null 調用方法:sayHello以後 調用方法:getName以前 返回值:A 調用方法:getName以後 A動態代理對象的類型:$Proxy0 SleepHandler.invoke 調用方法:goToSleep以前 i am go to sleep SleepHandler.invoke 返回值:null SleepHandler.invoke 調用方法:goToSleep以後 SleepHandler.invoke 調用方法:getUp以前 i am get up SleepHandler.invoke 返回值:null SleepHandler.invoke 調用方法:getUp以後
解析:
代理類一樣須要持有一個真實代理對象
當咱們但願建立一個代理類實例時,須要調用 Proxy.newProxyInstance(loader, interfaces, handler);方法
三個參數:
loader 被代理對象的類加載器,這裏存有真實對象的父類,子類關係,和其餘類的優先加載關係等等。
interfaces 被代理對象實現的全部接口,JDK提供的動態代理,說白了,就是讓代理類也去實現被代理類的全部接口,重寫被代理類的全部方法來實現的。
handler 咱們自定義的代理類實例,它實現了InvocationHandler接口。用來處理Proxy中全部方法的調用
首先解釋一下JDK動態代理的大體流程,JDK主要會作如下工做:
整理一下邏輯:
代理類實例建立以前,須要傳入一個自定義的handler實例,而自定義的handler類須要實現InvocationHandler接口,重寫invoke方法,在重寫的invoke方法中,增長自定義邏輯。
代理類實例建立時,是Proxy.newInstance(loader, interface[], handler)方法根據傳入的被代理類的類加載器信息,實現的全部接口,在jvm中動態的生成字節碼文件和.class文件。
代理類實例建立後,又須要強轉類型,向上造型爲被代理類的某個接口類型,強制轉換的是被代理類實現的某個接口類型,就能夠調用這個接口的方法。若是強轉的類型不是被代理類實現過的,就會有異常。
問題:
JDK是如何生成最終的代理類的,其實最根本的是經過動態生成代理類的字節碼,而後經過這些字節碼實現代理類的生成。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); public static byte[] generateProxyClass(final String name, Class[] interfaces) { ProxyGenerator gen = new ProxyGenerator(name, interfaces); // 這裏動態生成代理類的字節碼 final byte[] classFile = gen.generateClassFile(); // 若是saveGeneratedFiles的值爲true,則會把所生成的代理類的字節碼保存到硬盤上 if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class"); file.write(classFile); file.close(); return null; } catch (IOException e) { throw new InternalError( "I/O exception saving generated file: " + e); } } }); } // 返回代理類的字節碼 return classFile; }
代理類實例是怎麼調用hanler的invoke方法的?咱們來看一下代理類的class文件
public final void hello(String paramString) { try { this.h.invoke(this, m3, new Object[] { paramString }); //就是調用咱們自定義的InvocationHandlerImpl的 invoke方法: return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
注意這裏的this.h.invoke中的h,它是類Proxy中的一個屬性
protected InvocationHandler h;
由於這個代理類繼承了Proxy,因此也就繼承了這個屬性,而這個屬性值就是咱們定義的
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
同時咱們還發現,invoke方法的第一參數在底層調用的時候傳入的是this,也就是最終生成的代理對象ProxySubject,這是JVM本身動態生成的,而不是咱們本身定義的代理對象。
JDK的動態代理經過ProxyGenerator.generateProxyClass()生成代理類的字節碼,由Proxy.defineClass0()加載,能夠看到這個代理類 extends Proxy implements Interface1, ... {...}。
而在代理類中,與Proxy有關的只有Proxy的變量protected InvocationHandler h;這個InvocationHandler實例的引用,在調用接口方法時實際調用的是super.h.invoke(this, method, args)。
缺點:使用JDK動態代理,目標類必須實現的某個接口,若是某個類沒有實現接口則不能生成代理對象。
(Code Generation Library)是一個開源項目,是一個強大的,高性能,高質量的Code生成類庫,它能夠在運行期擴展Java類與實現Java接口,通俗說cglib能夠在運行時動態生成字節碼。
使用cglib完成動態代理,大概的原理是:cglib繼承被代理的類,重寫方法,織入通知,動態生成字節碼並運行,由於是繼承因此final類和方法是沒有辦法動態代理的。具體實現以下
建立被代理類:
package proxy; public class Person { public void sayHello() { System.out.println("my name is Person"); } public String getName() { return "Person"; } }
建立代理類:
package proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /* * 動態代理類 * 實現了一個方法攔截器接口 */ public class CglibProxyIntercepter implements MethodInterceptor { /* * sub:cglib生成的代理對象,method:被代理對象方法,objects:方法入參,methodProxy:代理方法 */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("執行前..."); // 調用方法 Object result = methodProxy.invokeSuper(proxy, args); System.out.println("執行後..."); return result; } // 返回目標對象的代理對象 public Object newProxy(Object target) { // 加強類,用於生成代理類的class文件 Enhancer enhancer = new Enhancer(); // 設置代理類的父類爲被代理類 enhancer.setSuperclass(target.getClass()); // 設置代理類的回調方法,即這裏指定了哪一個類,就會執行哪一個類的intercept方法 enhancer.setCallback(this); // 設置被代理類的加載器,保證生成類的順序和關係 enhancer.setClassLoader(target.getClass().getClassLoader()); // 生成代理類並返回實例 return enhancer.create(); } }
解析:
全部的代理類,都須要實現MethodIntercpet接口,重寫 intercept()方法,在方法中編寫自定義邏輯。
且經過methodProxy.invokeSuper()方法執行被代理類方法。
Enhancer是一個加強類,經過設置被代理類爲superClass,並指定由哪一個方法攔截器來成爲回調函數callBack,在調用creat()方法,動態生成代理類實例。
public class Test { public static void main(String[] args) { Person test = new Person(); CglibProxyIntercepter methodIntercpt= new CglibProxyIntercepter(); Person proxy2 = (Person) methodIntercpt.newProxy(test); proxy2.sayHello(); proxy2.getName(); } } 輸出結果: 執行前... my name is Person 執行後... 執行前... 執行後...
代理類實例也須要強制轉型,纔可使用。能夠轉型爲被代理類類型,和被代理類的接口類型,父類型。由於代理類是被代理類的子類。
下面的源碼是粘貼別人的,咱們來看一下,生成一個代理類會生成三個class文件
public class PersonService$$EnhancerByCGLIB$$eaaaed75 extends PersonService implements Factory { private boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0;//攔截器 private static final Method CGLIB$setPerson$0$Method;//被代理方法 private static final MethodProxy CGLIB$setPerson$0$Proxy;//代理方法 private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$finalize$1$Method; private static final MethodProxy CGLIB$finalize$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理類 Class localClass2;//被代理類PersionService Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$finalize$1$Method = tmp95_92[0]; CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1"); Method[] tmp115_95 = tmp95_92; CGLIB$equals$2$Method = tmp115_95[1]; CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); Method[] tmp135_115 = tmp115_95; CGLIB$toString$3$Method = tmp135_115[2]; CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); Method[] tmp155_135 = tmp135_115; CGLIB$hashCode$4$Method = tmp155_135[3]; CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4"); Method[] tmp175_155 = tmp155_135; CGLIB$clone$5$Method = tmp175_155[4]; CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); tmp175_155; Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods()); CGLIB$setPerson$0$Method = tmp223_220[0]; CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0"); tmp223_220; return; }
咱們經過代理類的源碼能夠看到,代理類會得到全部在父類繼承來的方法,而且會有MethodProxy與之對應,好比 Method CGLIB$setPerson$0$Method、MethodProxy CGLIB$setPerson$0$Proxy;
//代理方法(methodProxy.invokeSuper會調用) final void CGLIB$setPerson$0() { super.setPerson(); } //被代理方法(methodProxy.invoke會調用,這就是爲何在攔截器中調用methodProxy.invoke會死循環,一直在調用攔截器) public final void setPerson() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { //調用攔截器 var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy); } else { super.setPerson(); } }
調用過程:代理對象調用this.setPerson方法->調用攔截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理對象setPerson方法