当前位置: 首页 > news >正文

[Java] 模拟Jdk 以及 CGLib 代理原理

文章目录

  • 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});}
}

相关文章:

  • Ribbon负载均衡
  • KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(21)
  • C++分数计算器
  • 为何要3次握手?TCP协议的稳定性保障机制
  • 02.PostgreSQL 查询处理期间发生了什么?
  • 实现一个简单的网络通信下(udp)
  • 计算机网络——传输层
  • C++ 智能指针和内存管理:使用指南和技巧
  • 鸿蒙App开发 HarmonyOS:网络请求+三方库使用+底栏切换+列表+Banner
  • Python 错误 TypeError: __str__ Returned Non-String but Printing Output
  • Redis中缓存穿透、击穿、雪崩以及解决方案
  • PHP如何实现邮箱验证
  • 如何解决ajax浏览器缓存
  • 微信小程序保存二维码的过程
  • 构建第一个ArkTS应用(纯HarmonyOS应用)
  • 【391天】每日项目总结系列128(2018.03.03)
  • Centos6.8 使用rpm安装mysql5.7
  • ECS应用管理最佳实践
  • go append函数以及写入
  • gulp 教程
  • iOS编译提示和导航提示
  • Laravel 实践之路: 数据库迁移与数据填充
  • miaov-React 最佳入门
  • Promise面试题,控制异步流程
  • Python3爬取英雄联盟英雄皮肤大图
  • spring boot 整合mybatis 无法输出sql的问题
  • Spring核心 Bean的高级装配
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • win10下安装mysql5.7
  • Windows Containers 大冒险: 容器网络
  • 笨办法学C 练习34:动态数组
  • 如何合理的规划jvm性能调优
  • 实习面试笔记
  • 树莓派 - 使用须知
  • 一些关于Rust在2019年的思考
  • 找一份好的前端工作,起点很重要
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • ​Python 3 新特性:类型注解
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • # 安徽锐锋科技IDMS系统简介
  • #宝哥教你#查看jquery绑定的事件函数
  • (zhuan) 一些RL的文献(及笔记)
  • (二十四)Flask之flask-session组件
  • (转)Sql Server 保留几位小数的两种做法
  • (转)负载均衡,回话保持,cookie
  • (转载)利用webkit抓取动态网页和链接
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .NET 8.0 中有哪些新的变化?
  • .net core Swagger 过滤部分Api
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .Net多线程总结
  • /etc/motd and /etc/issue
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  • []利用定点式具实现:文件读取,完成不同进制之间的