先看看静态代理是如何操作的
定义接口:
1 public interface Person {2 public void sayHello(String content, int age);3 public void sayGoodBye(boolean seeAgin, double time);4 }
实际的类:
1 public class Student implements Person{ 2 3 @Override 4 public void sayHello(String content, int age) { 5 // TODO Auto-generated method stub 6 System.out.println("student say hello" + content + " "+ age); 7 } 8 9 @Override10 public void sayGoodBye(boolean seeAgin, double time) {11 // TODO Auto-generated method stub12 System.out.println("student sayGoodBye " + time + " "+ seeAgin);13 } 14 }
代理类:
1 public class ProxyTest implements Person{ 2 3 private Person o; 4 5 public ProxyTest(Person o){ 6 this.o = o; 7 } 8 9 @Override10 public void sayHello(String content, int age) {11 // TODO Auto-generated method stub12 System.out.println("ProxyTest sayHello begin");13 //在代理类的方法中 间接访问被代理对象的方法14 o.sayHello(content, age);15 System.out.println("ProxyTest sayHello end");16 }17 18 @Override19 public void sayGoodBye(boolean seeAgin, double time) {20 // TODO Auto-generated method stub21 System.out.println("ProxyTest sayHello begin");22 //在代理类的方法中 间接访问被代理对象的方法23 o.sayGoodBye(seeAgin, time);24 System.out.println("ProxyTest sayHello end");25 }26 27 public static void main(String[] args) {28 // TODO Auto-generated method stub29 //s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问30 Student s = new Student();31 //创建代理类对象32 ProxyTest proxy = new ProxyTest(s);33 //调用代理类对象的方法34 proxy.sayHello("welcome to java", 20);35 System.out.println("******");36 //调用代理类对象的方法37 proxy.sayGoodBye(true, 100);38 39 }40 41 }
可以看到,静态代理类要求实现与实际类型相同的接口,这个虽然在某些情况下有使用场景,但是其实扩展起来很麻烦,需要一个个的进行重载,相比之下,动态代理就好多了。
动态代理也需要一个代理类,实现特定的接口InvocationHandler:
1 public class MyInvocationHandler implements InvocationHandler{ 2 3 private Object object; 4 5 public MyInvocationHandler(Object object){ 6 this.object = object; 7 } 8 9 @Override10 public Object invoke(Object proxy, Method method, Object[] args)11 throws Throwable {12 // TODO Auto-generated method stub13 System.out.println("MyInvocationHandler invoke begin");14 System.out.println("proxy: "+ proxy.getClass().getName());15 System.out.println("method: "+ method.getName());16 for(Object o : args){17 System.out.println("arg: "+ o);18 }19 //通过反射调用 被代理类的方法20 method.invoke(object, args);21 System.out.println("MyInvocationHandler invoke end");22 return null;23 }24 25 public static void main(String [] args){26 //创建需要被代理的类27 Student s = new Student();28 //这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常29 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");30 //获得加载被代理类的 类加载器31 ClassLoader loader = Thread.currentThread().getContextClassLoader();32 //指明被代理类实现的接口33 Class [] interfaces = s.getClass().getInterfaces();34 // 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法35 MyInvocationHandler h = new MyInvocationHandler(s);36 //生成代理类37 Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);38 //通过代理类调用 被代理类的方法39 proxy.sayHello("yujie.wang", 20);40 proxy.sayGoodBye(true, 100);41 System.out.println("end");42 }43 44 }
这个灵活性就好多了,不用关心自己代理的类到底有多少方法,只用实现一个invoke方法,去做自己想做的事情就好,如果需要根据特定的方法做事情,可能就需要根据method判断了,不如静态代理写在重载方法中感觉好。
那么,java到底是怎么实现动态代理的,不妨反编译一下,拿到的结果如下,可以看到其实是jvm帮你做了静态代理的事情:
1 public final class $Proxy0 extends Proxy implements Person{ 2 private static Method m4; 3 private static Method m1; 4 private static Method m0; 5 private static Method m3; 6 private static Method m2; 7 8 public $Proxy0(InvocationHandler paramInvocationHandler) 9 throws 10 { 11 super(paramInvocationHandler); 12 } 13 //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用 14 public final void sayGoodBye(boolean paramBoolean, double paramDouble) 15 throws 16 { 17 try 18 { 19 // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法 20 // m4为代理类通过反射获得的Method 21 this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) }); 22 return; 23 } 24 catch (Error|RuntimeException localError) 25 { 26 throw localError; 27 } 28 catch (Throwable localThrowable) 29 { 30 throw new UndeclaredThrowableException(localThrowable); 31 } 32 } 33 34 public final boolean equals(Object paramObject) 35 throws 36 { 37 try 38 { 39 return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); 40 } 41 catch (Error|RuntimeException localError) 42 { 43 throw localError; 44 } 45 catch (Throwable localThrowable) 46 { 47 throw new UndeclaredThrowableException(localThrowable); 48 } 49 } 50 51 public final int hashCode() 52 throws 53 { 54 try 55 { 56 return ((Integer)this.h.invoke(this, m0, null)).intValue(); 57 } 58 catch (Error|RuntimeException localError) 59 { 60 throw localError; 61 } 62 catch (Throwable localThrowable) 63 { 64 throw new UndeclaredThrowableException(localThrowable); 65 } 66 } 67 //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用 68 public final void sayHello(String paramString, int paramInt) 69 throws 70 { 71 try 72 { 73 // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法 74 // m4为代理类通过反射获得的Method 75 this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) }); 76 return; 77 } 78 catch (Error|RuntimeException localError) 79 { 80 throw localError; 81 } 82 catch (Throwable localThrowable) 83 { 84 throw new UndeclaredThrowableException(localThrowable); 85 } 86 } 87 88 public final String toString() 89 throws 90 { 91 try 92 { 93 return (String)this.h.invoke(this, m2, null); 94 } 95 catch (Error|RuntimeException localError) 96 { 97 throw localError; 98 } 99 catch (Throwable localThrowable)100 {101 throw new UndeclaredThrowableException(localThrowable);102 }103 }104 105 static106 {107 try108 { //代理类通过反射 获得的接口方法Method109 m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE });110 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });111 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);112 //代理类通过反射 获得的接口方法Method113 m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE });114 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);115 return;116 }117 catch (NoSuchMethodException localNoSuchMethodException)118 {119 throw new NoSuchMethodError(localNoSuchMethodException.getMessage());120 }121 catch (ClassNotFoundException localClassNotFoundException)122 {123 throw new NoClassDefFoundError(localClassNotFoundException.getMessage());124 }125 }126 }
本文参考了https://blog.csdn.net/u011784767/article/details/78281384