jdk動態代理底層實現
一、代理設計模式
代理設計模式是Java常用的設計模式之一。
特點:
01.委托類和代理類有共同的接口或者父類;
02.代理類負責為委托類處理消息,並將消息轉發給委托類;
03.委托類和代理類對象通常存在關聯關系,一個代理類對象與一個委托類對象關聯;
04.代理類本身不是真正的實現者,而是通過調用委托類方法來實現代理功能;
二、靜態代理與動態代理
按照代理類創建的時機,代理類分為兩種:
01.靜態代理:由我們程序猿或者特定的工具自動生成了源代碼,在程序運行之前,class文件已經存在了;例如在serviceImpl.java中調用dao.xx(),真正的實現者是dao,service就可以理解為一個代理類;
02.動態代理:在程序運行期間,通過反射創建出來的代理類;
三、jdk動態代理
顧名思義,這種方式是由jdk為我們提供的。下面通過一個例子來演示。
01.創建一個Person接口
public interface Person { void eat(); void sleep(); }
02.創建ZhangSan.java實現Person接口
public class ZhangSan implements Person { public void eat() { System.out.println("吃飯..."); } publicvoid sleep() { System.out.println("睡覺..."); } }
03.創建代理類ZSProxy.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ZSProxy implements InvocationHandler { //被代理類對象引用 private ZhangSan zhangSan; public ZSProxy(ZhangSan zhangSan) {super(); this.zhangSan = zhangSan; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); //zhangSan.eat(); Object result = method.invoke(zhangSan, args);//可以獲取目標方法的返回值 after(); return null; } private void before() { System.out.println("前置..."); } private void after() { System.out.println("後置..."); } }
jdk動態代理中必須了解一個類和一個接口:Proxy類和InvocationHandler接口。
001.上述中的代理類實現了 InvocationHandler接口,此接口只有一個invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
proxy:代理類對象
method:被代理的方法
args:被代理方法的參數列表
002.Proxy類
public class Proxy implements java.io.Serializable { ... public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException ... }
loader:類加載器
interfaces:代理類實現的所有接口
h:InvocationHandler接口的一個實例
public class Test { public static void main(String[] args) throws Throwable { System.out.println("----------------------JDK動態代理----------------------------"); //獲取代理類對象 Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] {Person.class}, new ZSProxy(new ZhangSan())); proxyInstance.eat(); }
通過Proxy的newProxyInstance方法創建出代理對象,再有代理對象執行方法。
程序運行結果:
雖然效果實現了,但我們並不能從代碼看到哪裏調用的invoke方法??那麽底層到底是怎麽執行的呢??
首先要了解一個類 ==》$Proxy0.java
001. $Proxy0 是內存中的代理類,在$Proxy0中會持有一個InvocationHandler接口的實例類的引用,所以此Test類先是調用了內存中的$Proxy0.eat();
002.執行$Proxy0類中的invoke
我們debug運行觀察:
內存中代理類的特征:
01.它是對目標類的代理,那麽這個內存中中的代理類的類型必須和被代理類(目標類)的類型一致。
代碼中 (Person)Proxy.newProxyInstance.. 進行了向下轉型操作。
02.內存中的代理類必須持有InvocationHandler接口的實現類引用。
整個過程中$Proxy0我們是看不到的!那麽有沒有辦法讓它原形畢露呢?
/** * 使用IO的方式將內存中代理類寫入到文件中,然後反編譯出來進行觀察 */ private static void createProxyClassFile() { byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class}); try { FileOutputStream out = new FileOutputStream("$Proxy0.class"); out.write(data); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
對$Proxy0.java反編譯後
1 public final class class extends Proxy 2 implements Person 3 { 4 5 private static Method m1; 6 private static Method m4; 7 private static Method m2; 8 private static Method m3; 9 private static Method m0; 10 11 public class(InvocationHandler invocationhandler) //通過構造將handler傳入 12 { 13 super(invocationhandler); 14 } 15 16 public final boolean equals(Object obj) 17 { 18 try 19 { 20 return ((Boolean)super.h.invoke(this, m1, new Object[] { 21 obj 22 })).booleanValue(); 23 } 24 catch (Error ) { } 25 catch (Throwable throwable) 26 { 27 throw new UndeclaredThrowableException(throwable); 28 } 29 } 30 31 public final void eat() 32 { 33 try 34 { 35 super.h.invoke(this, m4, null);// h:就是上文說的 此類中必須存在InvocationHandler接口實現類的引用,也是這裏真正調用了invoke方法 36 return; 37 } 38 catch (Error ) { } 39 catch (Throwable throwable) 40 { 41 throw new UndeclaredThrowableException(throwable); 42 } 43 } 44 45 public final String toString() 46 { 47 try 48 { 49 return (String)super.h.invoke(this, m2, null); 50 } 51 catch (Error ) { } 52 catch (Throwable throwable) 53 { 54 throw new UndeclaredThrowableException(throwable); 55 } 56 } 57 58 public final void sleep() 59 { 60 try 61 { 62 super.h.invoke(this, m3, null); 63 return; 64 } 65 catch (Error ) { } 66 catch (Throwable throwable) 67 { 68 throw new UndeclaredThrowableException(throwable); 69 } 70 } 71 72 public final int hashCode() 73 { 74 try 75 { 76 return ((Integer)super.h.invoke(this, m0, null)).intValue(); 77 } 78 catch (Error ) { } 79 catch (Throwable throwable) 80 { 81 throw new UndeclaredThrowableException(throwable); 82 } 83 } 84 85 static 86 { 87 try 88 { 89 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { 90 Class.forName("java.lang.Object") 91 }); 92 m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]); 93 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 94 m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]); 95 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 96 } 97 catch (NoSuchMethodException nosuchmethodexception) 98 { 99 throw new NoSuchMethodError(nosuchmethodexception.getMessage()); 100 } 101 catch (ClassNotFoundException classnotfoundexception) 102 { 103 throw new NoClassDefFoundError(classnotfoundexception.getMessage()); 104 } 105 } 106 }
那麽就真相大白了。
********************************************************************************************************************************************
以上就是jdk為我們提供的動態代理。我們也可以模仿它的實現原理,自定義我們的動態代理。
01.創建Person接口
package cn.yzx.myProxy; public interface Person { void eat() throws Throwable; void sleep() throws Throwable; }
02.創建ZhangSan實現類
package cn.yzx.myProxy; public class ZhangSan implements Person { public void eat() { System.out.println("吃飯..."); } public void sleep() { System.out.println("睡覺..."); } }
03.ZSProxy代理類
package cn.yzx.myProxy; import java.lang.reflect.Method; public class ZSProxy implements MyInvocationHandler { //目標對象 private Person person; public ZSProxy(Person person) { this.person = person; } @Override public Object invoke(Object proxy, Method method, Object args) { before(); try { person.eat(); } catch (Throwable e) { e.printStackTrace(); } after(); return null; } private void before() { System.out.println("前置..."); } private void after() { System.out.println("後置..."); } }
04.自定義動態代理Proxy類
1 package cn.yzx.myProxy; 2 3 import java.io.File; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 import java.lang.reflect.Constructor; 7 import java.lang.reflect.InvocationTargetException; 8 import java.lang.reflect.Method; 9 10 import javax.tools.JavaCompiler; 11 import javax.tools.JavaCompiler.CompilationTask; 12 import javax.tools.StandardJavaFileManager; 13 import javax.tools.ToolProvider; 14 15 16 /** 17 * 自定義的動態代理Proxy類 18 * @author Xuas 19 */ 20 public class MyProxy { 21 static String rt = "\r\n"; 22 /** 23 * 自定義創建內存中代理實例的方法 24 * @param loader 25 * @param interfaces 26 * @param handler 27 * @return 28 */ 29 public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) { 30 31 try { 32 Method[] methods = interfaces.getMethods(); 33 //01.使用拼湊字符串的方式將內存中的代理類拼出來 34 String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt 35 + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt 36 + "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}" 37 + getMethodString(methods, interfaces) + rt + "}"; 38 //02.使用IO將拼湊的代理類寫入到文件中 39 String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java"; 40 File file = new File(filePathName); 41 FileWriter fw = new FileWriter(file); 42 fw.write(proxyClass); 43 fw.flush(); 44 fw.close(); 45 46 //03.編譯上面生成的java文件 47 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 48 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, 49 null, null);//文件管理器 50 Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//獲取java文件 51 CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//獲取編譯任務 52 task.call();//編譯 53 fileManager.close();//關閉文件管理器 54 /** 55 * 此時被編譯後的class文件還在硬盤之中!它應該存在於JVM內存中! 56 * 所以要把硬盤中class文件加載到JVM內存中去! 57 */ 58 //04.把硬盤中的class文件加載到內存中去,要使用ClassLoader 59 MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy"); 60 61 //proxy0Clazz:就是內存中代理類的class對象 62 Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理類的class對象 63 /** 64 * public $Proxy0(MyInvocationHandler h) {..} 65 * 要通過構造器的參數將handler對象的引用傳進來 66 */ 67 Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class); 68 Object instance = constructor.newInstance(handler);//返回內存中代理類實例 69 return instance; 70 } catch (IOException e) { 71 e.printStackTrace(); 72 } catch (ClassNotFoundException e) { 73 e.printStackTrace(); 74 } catch (NoSuchMethodException e) { 75 e.printStackTrace(); 76 } catch (SecurityException e) { 77 e.printStackTrace(); 78 } catch (InstantiationException e) { 79 e.printStackTrace(); 80 } catch (IllegalAccessException e) { 81 e.printStackTrace(); 82 } catch (IllegalArgumentException e) { 83 e.printStackTrace(); 84 } catch (InvocationTargetException e) { 85 e.printStackTrace(); 86 } 87 return null; 88 } 89 90 private static String getMethodString(Method[] methods, Class interfaces) { 91 92 String proxyMe = ""; 93 94 for (Method method : methods) { 95 proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = " 96 + interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt 97 + "this.h.invoke(this,md,null);" + rt + "}" + rt; 98 } 99 100 return proxyMe; 101 } 102 }
05.自定義InvocationHandler接口
1 package cn.yzx.myProxy; 2 3 import java.lang.reflect.Method; 4 5 /** 6 * 自定義InvocationHandler接口 7 * @author Xuas 8 * 9 */ 10 public interface MyInvocationHandler { 11 public Object invoke(Object proxy, Method method, Object args); 12 }
06.自定義的類加載器
package cn.yzx.myProxy; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class MyClassLoader extends ClassLoader { private File dir; public MyClassLoader(String path) {//要加載的文件路徑 dir = new File(path); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if(null != dir) { File clazzFile = new File(dir, name + ".class"); if(clazzFile.exists()) { try { FileInputStream input = new FileInputStream(clazzFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while((len = input.read(buffer)) != -1) { baos.write(buffer, 0, len); } return defineClass("cn.yzx.myProxy."+name, baos.toByteArray(), 0, baos.size());//最終把輸出流輸出到JVM內存中 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } return super.findClass(name); } }
07.內存中自定義生成的 $Proxy0.java
1 package cn.yzx.myProxy; 2 import java.lang.reflect.Method; 3 public class $Proxy0 implements cn.yzx.myProxy.Person{ 4 MyInvocationHandler h; 5 public $Proxy0(MyInvocationHandler h) { 6 this.h = h; 7 }public void sleep() throws Throwable { 8 Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{}); 9 this.h.invoke(this,md,null); 10 } 11 public void eat() throws Throwable { 12 Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{}); 13 this.h.invoke(this,md,null); 14 } 15 16 }
08.測試類
public class Test { public static void main(String[] args) throws Throwable { System.out.println("----------------------自定義動態代理----------------------------"); cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(), cn.yzx.myProxy.Person.class, new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan())); person2.eat(); } }
程序運行結果:
************************************************************************************************************************************************
總結:
通過觀察Proxy中的newProxyInstance方法的參數可知,jdk動態代理只支持委托類和代理類實現共同的接口的方式。
如果是實現共同父類的情況,不能使用jdk動態代理。可以使用cglib動態代理。
既然寫到這了,那我就再簡單介紹下cglib動態代理吧 -。 -
cglib動態代理(接口和父類的情況都可行)
兩個關鍵因素:
01.MethodInterceptor接口中的interceptor方法。
public Object intercept(Object obj, Method method,Object[] args,MethodProxy proxy) throws Throwable;
interceptor是所有攔截器執行的方法,類似於jdk動態代理中的invoke
02.Enhancer類
public void setSuperclass(Class superclass) { if (superclass != null && superclass.isInterface()) { setInterfaces(new Class[]{ superclass }); } else if (superclass != null && superclass.equals(Object.class)) { // affects choice of ClassLoader this.superclass = null; } else { this.superclass = superclass; } }
設置委托類和代理類的公共接口或者公共父類。
代理類執行完畢後,通知委托類:
public void setCallback(final Callback callback) { setCallbacks(new Callback[]{ callback }); }
在Enhancer的父類AbstractClassGenerator中有一個方法 作用是創建需要的代理類
protected Object create(Object key)
寫個例子:
01.創建父類Animal
package cn.yzx.cglib; public abstract class Animal { abstract String eat(); }
02.實現類Cat
package cn.yzx.cglib; public class Cat extends Animal{ public String eat() { System.out.println("cat吃飯"); return "haha"; } }
03.代理類
package cn.yzx.cglib; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.junit.Test; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); /** * 創建代理類對象 * 參數:委托類Class對象 * 返回值:代理類對象 */ public Object createProxyObject(Class<?> clazz){ enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } /** * 代理類執行委托類方法 */ public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("前置"); Object result = methodProxy.invokeSuper(o,args); System.out.println("後置"); System.out.println(result); return result; } /** * 測試方法 */ @Test public void test(){ CglibProxy proxy = new CglibProxy(); Animal cat = (Animal) proxy.createProxyObject(Cat.class); cat.eat(); } }
測試結果:
ok...
就醬吧
jdk動態代理底層實現