文章目录
- JDK
- arthas 反编译jdk代理对象
- arthas 反编译的结果是:
- CGlib
- methodProxy 不经过反射调用方法的原理
- MethodProxy原理
- 模拟 结合目标对象使用
- 模拟结合 代理对象使用
JDK
Jdk代理的最简单模拟, 由前文可知 JDK动态代理需要实现接口,所以基于此,进行最简单的模拟。
package com.example.proxy;public class Jdk {interface Foo {void foo();}static class Target implements Foo {@Overridepublic void foo() {System.out.println("foo");}}// 代理类static class $Proxy0 implements Foo {@Overridepublic 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;}@Overridepublic void foo() {// 1. 功能增强System.out.println("before");// 2. 调用目标invokeHandler.invoke();}}public static void main(String[] args) {Foo f = new $Proxy0(new InvokeHandler() {@OverrideObject 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;}@Overridepublic void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);}@Overridepublic 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 {@Overridepublic void foo() {System.out.println("target foo");}@Overridepublic void bar() {System.out.println("target bar");}}public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Foo f = new $Proxy0(new InvokeHandler() {@OverrideObject 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;}@Overridepublic Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 1. 功能增强System.out.println("before");// 2. 调用目标return invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);}@Overridepublic 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 {@Overridepublic Integer foo() {System.out.println("target foo");return 1;}@Overridepublic 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() {@OverrideObject 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;}@Overridepublic 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]);}@Overridepublic 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 {@Overridepublic Integer foo() {System.out.println("target foo");return 1;}@Overridepublic 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() {@OverrideObject 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反编译,看到字节码。
arthas 反编译jdk代理对象
比如:
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 {@Overridepublic 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 A p p C l a s s L o a d e r @ 251 a 69 d 7 + − j d k . i n t e r n a l . l o a d e r . C l a s s L o a d e r s AppClassLoader@251a69d7 +-jdk.internal.loader.ClassLoaders AppClassLoader@251a69d7+−jdk.internal.loader.ClassLoadersPlatformClassLoader@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);}}}
CGlib
cglib 代理类继承目标对象。
public class Target {public void save() {System.out.println("0");}public void save(int i) {System.out.println(i);}public void save(long l) {System.out.println(l);}
}//- cglib 代理模拟
public class Proxy extends Target{private MethodInterceptor methodInterceptor;public void setMethodInterceptor(MethodInterceptor methodInterceptor) {this.methodInterceptor = methodInterceptor;}private static final Method save0;private static final Method save1;private static final Method save2;static {try {save0 = Target.class.getMethod("save");save1 = Target.class.getMethod("save", int.class);save2 = Target.class.getMethod("save", long.class);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}@Overridepublic void save() {try {methodInterceptor.intercept(this, save0, new Object[0], null);} catch (Throwable e) {throw new RuntimeException(e);}}@Overridepublic void save(int i) {try {methodInterceptor.intercept(this, save1, new Object[]{i}, null);} catch (Throwable e) {throw new RuntimeException(e);}}@Overridepublic void save(long l) {try {methodInterceptor.intercept(this, save2, new Object[]{l}, null);} catch (Throwable e) {throw new RuntimeException(e);}}
}//- 测试类
public class ProxyTest {public static void main(String[] args) {Target target = new Target();Proxy proxy = new Proxy();proxy.setMethodInterceptor((obj, method, args1, proxy1) -> {System.out.println("before----");return method.invoke(target, args1);});proxy.save();proxy.save(1);proxy.save(2L);}
}
methodProxy 不经过反射调用方法的原理
在在上述 Proxy 类中,重写了父类中的方法,并在重写的方法中调用了 intercept() 方法,重写的这些方法相当于是带增强功能的方法。
在 JDK 的动态代理中,使用反射对方法进行调用,而在 CGLib 动态代理中,可以使用 intercept() 方法中 MethodProxy 类型的参数实现不经过反射来调用方法。
接收的 MethodProxy 类型的参数可以像 Method 类型的参数一样,在静态代码块中被实例化。
public class Proxy extends Target{private MethodInterceptor methodInterceptor;public void setMethodInterceptor(MethodInterceptor methodInterceptor) {this.methodInterceptor = methodInterceptor;}static Method save0;static Method save1;static Method save2;static MethodProxy save0Proxy;static MethodProxy save1Proxy;static MethodProxy save2Proxy;static {try {save0 = Target.class.getMethod("save");save1 = Target.class.getMethod("save", int.class);save2 = Target.class.getMethod("save", long.class);save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");} catch (NoSuchMethodException e) {throw new NoSuchMethodError(e.getMessage());}}// >>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法public void saveSuper() {super.save();}public void saveSuper(int i) {super.save(i);}public void saveSuper(long i) {super.save(i);}// >>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法@Overridepublic void save() {try {methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}@Overridepublic void save(int i) {try {methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}@Overridepublic void save(long i) {try {methodInterceptor.intercept(this, save2, new Object[]{i}, save2Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}
}//- 测试类
public class ProxyTest {public static void main(String[] args) {Target target = new Target();Proxy proxy = new Proxy();proxy.setMethodInterceptor((obj, method, args1, methodProxy) -> {System.out.println("before----");
// return method.invoke(target, args1);
// return methodProxy.invoke(target, args1); // 内部无反射调用 结合目标对象使用return methodProxy.invokeSuper(obj, args1); // 内部无反射调用, 结合代理对象使用});proxy.save();proxy.save(1);proxy.save(2L);}
}
MethodProxy原理
其内部是通过一个 FastClass+ 方法签名实现
模拟 结合目标对象使用
Target target = new Target();Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, args, methodProxy) -> {System.out.println("before...");// 内部没使用反射,需要目标(spring 的选择)Object result = methodProxy.invoke(target, args);System.out.println("after...");return result;
});
package com.example.proxy;import org.springframework.cglib.core.Signature;public class TargetFastClass {static Signature s0 = new Signature("save", "()V");static Signature s1 = new Signature("save", "(I)V");static Signature s2 = new Signature("save", "(J)V");/*** <p>获取目标方法的编号</p>* <p>* Target 目标类中的方法:* save() 0* save(int) 1* save(long) 2* </p>** @param signature 包含方法名称、参数返回值* @return 方法编号*/public int getIndex(Signature signature) {if (s0.equals(signature)) {return 0;}if (s1.equals(signature)) {return 1;}if (s2.equals(signature)) {return 2;}return -1;}/*** 根据 getIndex() 方法返回的方法编号正常调用目标对象方法** @param index 方法编号* @param target 目标对象* @param args 调用目标对象方法需要的参数* @return 方法返回结果*/public Object invoke(int index, Object target, Object[] args) {if (index == 0) {((Target) target).save();return null;}if (index == 1) {((Target) target).save((int) args[0]);return null;}if (index == 2) {((Target) target).save((long) args[0]);return null;}throw new RuntimeException("无此方法");}public static void main(String[] args) {TargetFastClass fastClass = new TargetFastClass();int index = fastClass.getIndex(new Signature("save", "()V"));fastClass.invoke(index, new Target(), new Object[0]);index = fastClass.getIndex(new Signature("save", "(J)V"));fastClass.invoke(index, new Target(), new Object[]{2L});}
}
模拟结合 代理对象使用
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, args, methodProxy) -> {System.out.println("before...");// 内部没使用反射,需要代理Object result = methodProxy.invokeSuper(obj, args);System.out.println("after...");return result;
});
package com.example.proxy;import org.springframework.cglib.core.Signature;public class ProxyFastClass {static Signature s0 = new Signature("saveSuper", "()V");static Signature s1 = new Signature("saveSuper", "(I)V");static Signature s2 = new Signature("saveSuper", "(J)V");/*** <p>获取代理方法的编号</p>* <p>* Proxy 代理类中的方法:* saveSuper() 0* saveSuper(int) 1* saveSuper(long) 2* </p>** @param signature 包含方法名称、参数返回值* @return 方法编号*/public int getIndex(Signature signature) {if (s0.equals(signature)) {return 0;}if (s1.equals(signature)) {return 1;}if (s2.equals(signature)) {return 2;}return -1;}/*** 根据 getIndex() 方法返回的方法编号正常调用代理对象中带原始功能的方法** @param index 方法编号* @param proxy 代理对象* @param args 调用方法需要的参数* @return 方法返回结果*/public Object invoke(int index, Object proxy, Object[] args) {if (index == 0) {((Proxy) proxy).saveSuper();return null;}if (index == 1) {((Proxy) proxy).saveSuper((int) args[0]);return null;}if (index == 2) {((Proxy) proxy).saveSuper((long) args[0]);return null;}throw new RuntimeException("无此方法");}public static void main(String[] args) {ProxyFastClass fastClass = new ProxyFastClass();int index = fastClass.getIndex(new Signature("saveSuper", "()V"));fastClass.invoke(index, new Proxy(), new Object[0]);int index1 = fastClass.getIndex(new Signature("saveSuper", "(J)V"));fastClass.invoke(index1, new Proxy(), new Object[]{2L});}
}