java 动态代理
0x01 描述
”动态代理中的动态,是针对使用java代码实际编写了代理类的“静态”代理而言的,它的优势不在于省去了编写代理类的那一点工作量,而是实现了可以在原始类和接口还未知的时候,就可以确定代理类的行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景中" 引自 《深入理解java虚拟机 java高级特性与最佳实践》。
它的缺点:因为java中类只支持单继承,而代理类有个共同的父类Proxy,所有注定了动态代理不能为类成员方法作代理的缺陷。
0x02 demo
package test; public interface IWorker { Object work(Object... objects); }
package test; public class Worker implements IWorker { @Override public Object work(Object... objects) { System.out.println("Working ..."); return "work method"; } }
package test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class Handler implements InvocationHandler { IWorker mWorker; Handler(IWorker iWorker) { this.mWorker = iWorker; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("equals")) { System.out.println("the equals method will always return true"); return true; } else if (method.getName().equals("hashCode")) { System.out.println("yes, the hashCode is "); } else if (method.getName().equals("toString")) { System.out.println("toString method has been called"); } return method.invoke(mWorker, args); } }
package test; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { System.getProperties().put( "sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); IWorker worker = (IWorker) Proxy.newProxyInstance( IWorker.class.getClassLoader(), Worker.class.getInterfaces(), new Handler(new Worker())); System.out.println(worker.work()); System.out.println(worker.equals(null)); System.out.println(worker.toString()); System.out.println(worker.hashCode()); } }
0x03 动态生成的代理类的代码
package com.sun.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import test.IWorker; public final class $Proxy0 extends Proxy implements IWorker { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { 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 toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final Object work(Object[] paramArrayOfObject) throws { try { return (Object)this.h.invoke(this, m3, new Object[] { paramArrayOfObject }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { 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") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("test.IWorker").getMethod("work", new Class[] { Class.forName("[Ljava.lang.Object;") }); 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()); } } }
0x04 注意
1、动态生成的方法都是final的
2、除了接口中的方法外,默认它还覆盖了Object类中的toString、hashCode和equals方法
3、每个方法都进行了异常的捕捉,虽然对异常进行了简单的分类处理,但是仍然使用了奇怪的语法将捕捉到的运行时异常照常抛出,所以在使用到代理类的地方照样可以使用正常的异常出来逻辑来处理运行时抛出的异常