从模仿到反编译 jdk 代理源码
Jdk代理的最简单模拟, 由前文可知 JDK动态代理需要实现接口,所以基于此,进行最简单的模拟。
package com.example.proxy; public class Jdk { interface Foo { void foo(); } static class Target implements Foo { @Override public void foo() { System.out.println("foo"); } } // 代理类 static class $Proxy0 implements Foo { @Override public void foo() { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 new Target().foo(); } } public static void main(String[] args) { Foo f = new $Proxy0(); f.foo(); } }
虽然简单实现了代理,但是目前增强是固定的,但是在实际应用中,使用到代理类,方法是不可能固定的,所以接下来进行优化一下。使用抽象类+模版方法设置代理的执行逻辑。
package com.example.proxy; public class Jdk { interface Foo { void foo(); } static abstract class InvokeHandler { abstract Object invoke(); } // 代理类 static class $Proxy0 implements Foo { private final InvokeHandler invokeHandler; $Proxy0(InvokeHandler invokeHandler) { this.invokeHandler = invokeHandler; } @Override public void foo() { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 invokeHandler.invoke(); } } public static void main(String[] args) { Foo f = new $Proxy0(new InvokeHandler() { @Override Object invoke() { System.out.println(">>>>>>>> foo"); return null; } }); f.foo(); } }
至此,方法就是可以不再固定。但是很显然,代理的对象不可能永远只有一个方法,所以想办法动态设置。
package com.example.proxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Jdk { interface Foo { void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; } static abstract class InvokeHandler { abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException; } // 代理类 static class $Proxy0 implements Foo { private final InvokeHandler invokeHandler; $Proxy0(InvokeHandler invokeHandler) { this.invokeHandler = invokeHandler; } @Override public void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]); } @Override public void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 invokeHandler.invoke(Foo.class.getMethod("bar"), new Object[0]); } } static class Target implements Foo { @Override public void foo() { System.out.println("target foo"); } @Override public void bar() { System.out.println("target bar"); } } public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Foo f = new $Proxy0(new InvokeHandler() { @Override Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException { // 传入代理对象 method.invoke(new Target(), params); return null; } }); f.foo(); f.bar(); } } /** 运行结果 before target foo before target bar **/
到这里,可以发现,多方法的代理对象也可以正常执行。但是如果执行方法有值返回呢,这个也简单,小修改一波。
package com.example.proxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Jdk { interface Foo { Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; } static abstract class InvokeHandler { abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException; } // 代理类 static class $Proxy0 implements Foo { private final InvokeHandler invokeHandler; $Proxy0(InvokeHandler invokeHandler) { this.invokeHandler = invokeHandler; } @Override public Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 return invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]); } @Override public Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 return invokeHandler.invoke(Foo.class.getMethod("bar"), new Object[0]); } } static class Target implements Foo { @Override public Integer foo() { System.out.println("target foo"); return 1; } @Override public String bar() { System.out.println("target bar"); return "hello"; } } public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Foo f = new $Proxy0(new InvokeHandler() { @Override Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException { // 传入代理对象 return method.invoke(new Target(), params); } }); System.out.println(f.foo()); System.out.println(f.bar()); } } /** 运行结果 before target foo 1 before target bar hello **/
在源码实现中,方法还可以被缓存复用,不需要每次都重新创建。
package com.example.proxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class Jdk { interface Foo { Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; } static abstract class InvokeHandler { abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException; } // 代理类 static class $Proxy0 implements Foo { private final InvokeHandler invokeHandler; private final Map<String, Method> cache = new HashMap<>(); $Proxy0(InvokeHandler invokeHandler) { this.invokeHandler = invokeHandler; } @Override public Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 Method foo = cache.getOrDefault("foo", null); if(foo == null) { foo = Foo.class.getMethod("foo"); System.out.println(">>>>>> 新创建方法"); cache.put("foo", foo); } return invokeHandler.invoke(foo, new Object[0]); } @Override public Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println("before"); // 2. 调用目标 Method bar = cache.getOrDefault("bar", null); if(bar == null) { bar = Foo.class.getMethod("foo"); System.out.println(">>>>>> 新创建方法"); cache.put("bar", bar); } return invokeHandler.invoke(bar, new Object[0]); } } static class Target implements Foo { @Override public Integer foo() { System.out.println("target foo"); return 1; } @Override public String bar() { System.out.println("target bar"); return "hello"; } } public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Foo f = new $Proxy0(new InvokeHandler() { @Override Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException { // 传入代理对象 return method.invoke(new Target(), params); } }); System.out.println(f.foo()); System.out.println(f.bar()); System.out.println(f.foo()); System.out.println(f.bar()); } } /** before >>>>>> 新创建方法 target foo 1 before >>>>>> 新创建方法 target foo 1 before target foo 1 before target foo 1 **/
到此,代理方法只会被寻找一次。
JDK 动态代理生成的代理类是以字节码的形式存在的,并不存在所谓的 .java 文件,但也不是说就没办法看到生成的代理类信息了。不过可以使用 arthas反编译,看到字节码。
比如:
package com.example.proxy; import java.io.IOException; import java.lang.reflect.Proxy; public class Jdk1 { interface Foo { void foo(); } static final class Target implements Foo { @Override public void foo() { System.out.println("target foo"); } } public static void main(String[] args) throws IOException { // 原始对象 Target target = new Target(); // 用来加载在运行期间动态生成的字节码 ClassLoader classLoader = Jdk1.class.getClassLoader(); Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (p, method, params) -> { System.out.println("before..."); // 目标.方法(参数) --> 方法.invoke(目标, 参数) Object result = method.invoke(target, params); System.out.println("after..."); // 也返回目标方法执行的结果 return result; }); // 打印代理类的全限定类名 System.out.println(proxy.getClass()); proxy.foo(); // 只要不在控制台上输入并回车,程序就不会终端 System.in.read(); } }
打印的结果是:
class com.example.proxy.$Proxy0 before... target foo after...
arthas 反编译的结果是:
[arthas@60054]$ jad com.example.proxy.$Proxy0 ClassLoader: +-jdk.internal.loader.ClassLoaders$AppClassLoader@251a69d7 +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@17747fbe Location: /* * Decompiled with CFR. * * Could not load the following classes: * com.example.proxy.Jdk1$Foo */ package com.example.proxy; import com.example.proxy.Jdk1; import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; final class $Proxy0 extends Proxy implements Jdk1.Foo { private static final Method m0; private static final Method m1; private static final Method m2; private static final Method m3; private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException { if (lookup.lookupClass() == Proxy.class && lookup.hasFullPrivilegeAccess()) { return MethodHandles.lookup(); } throw new IllegalAccessException(lookup.toString()); } public $Proxy0(InvocationHandler invocationHandler) { super(invocationHandler); } static { try { m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.example.proxy.Jdk1$Foo").getMethod("foo", new Class[0]); return; } catch (NoSuchMethodException noSuchMethodException) { throw new NoSuchMethodError(noSuchMethodException.getMessage()); } catch (ClassNotFoundException classNotFoundException) { throw new NoClassDefFoundError(classNotFoundException.getMessage()); } } public final void foo() { try { this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final boolean equals(Object object) { try { return (Boolean)this.h.invoke(this, m1, new Object[]{object}); } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return (Integer)this.h.invoke(this, m0, null); } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } }