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

浅谈动态代理

什么是动态代理?


以下为个人理解:

  • 动态代理就是在程序运行的期间,动态地针对对象的方法进行增强操作。并且这个动作的执行者已经不是"this"对象了,而是我们创建的代理对象,这个代理对象就是类似中间人的角色,帮助我们为目标方法嵌入一些其他的逻辑进去。

jdk动态代理的原理


  • jdk自带的动态代理的工作原理是利用反射的newInstance,创建一个代理对象(proxy),获取到目标接口的方法,然后我们就可以在Invoke之前或之后做操作。它抽取出了一个invokeHandler,里面就有目标method。

  • 试想一下,当我们拥有了class对象,增强逻辑(invokeHandler),也就是增强的目标方法之后,我们自己利用反射(不利用Proxy)也可以很容易写出我们自己的动态代理。但是问题就在于,我们只有明确了目标类之后,通过自己编写Proxy类,实现目标接口,往里面塞invoktionHandler, 最后New出来这个实实在在的对象。而jdk提供的动态代理,却可以在并不知情的情况下,帮我们做这一系列的动作。

  • jdk生成代理类时,并没有经历源码阶段,编译阶段,而是直接到字节码阶段,它生成的代理类是看不到的,因为它直接就是字节码文件了。 它帮我们继承了Proxy类,并且还动态的帮助我们继承了目标接口。也就是说,它帮我们写代码了。里面用到的技术,是ASM技术,它可以直接生成我们想要的字节码。

  • jdk方法反射调用优化:

  • invoke() 利用反射进行本地调用,效率低下,它在调用了一定的次数(16)之后会生成实例化对象,变成正常调用。

InvocationHandler接口

publicinterfaceInvocationHandler {

publicObjectinvoke(Objectproxy, Methodmethod, Object[] args)throwsThrowable;

}

源码

@CallerSensitive

publicstaticObjectnewProxyInstance(ClassLoaderloader,

Class<?>[] interfaces,

InvocationHandlerh)

throwsIllegalArgumentException

{

Objects.requireNonNull(h);

finalClass<?>[] intfs=interfaces.clone();

finalSecurityManagersm=System.getSecurityManager();

if (sm!=null) {

checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

}

/*

* Look up or generate the designated proxy class.

*/

Class<?>cl=getProxyClass0(loader, intfs);

/*

* Invoke its constructor with the designated invocation handler.

*/

try {

if (sm!=null) {

checkNewProxyPermission(Reflection.getCallerClass(), cl);

}

finalConstructor<?>cons=cl.getConstructor(constructorParams);

finalInvocationHandlerih=h;

if (!Modifier.isPublic(cl.getModifiers())) {

AccessController.doPrivileged(newPrivilegedAction<Void>() {

publicVoidrun() {

cons.setAccessible(true);

returnnull;

}

});

}

returncons.newInstance(newObject[]{h});

} catch (IllegalAccessException|InstantiationExceptione) {

thrownewInternalError(e.toString(), e);

} catch (InvocationTargetExceptione) {

Throwablet=e.getCause();

if (tinstanceofRuntimeException) {

throw (RuntimeException) t;

} else {

thrownewInternalError(t.toString(), t);

}

} catch (NoSuchMethodExceptione) {

thrownewInternalError(e.toString(), e);

}

}

cglib动态代理的原理


  • cglib动态代理的原理跟jdk的类似,只不过它是基于父类继承,也就是不需要实现接口就可以做增强。它的内部并不是InvoktionHandler,而是方法拦截器 MethodInterceptor 。

  • 前面的jdk动态代理,它是利用ASM技术帮我们动态编写了一个proxy对象,其中继承了Proxy父类,实现了目标接口,而cglib则是利用ASM直接帮助我们继承了目标类,不需要接口。

  • 并且有所区别的是,它不仅仅通过反射拿到method,还拿到了MethodProxy。

  • MethodInterceptor接口 继承了Callback接口,它的拦截方法里面有一个特殊的参数 MethodProxy,这玩意可以不通过反射调用方法,通过invokeSuper() 方法可以直接正常调用。

  • MethodProxy是怎么做到正常调用的?

  • 其实就是我们前面提到的,继承了Proxy父类之后,就得到了父类的原始方法,当我调用invokeSuper的时候,直接调用的就是父类的原始方法。

MethodInterceptor

publicinterfaceMethodInterceptorextendsCallback {

Objectintercept(Objectvar1, Methodvar2, Object[] var3, MethodProxyvar4) throwsThrowable;

}

MethodProxy

  • invoke() 无反射调用

  • invokeSuper() 无反射调用

publicObjectinvoke(Objectobj, Object[] args) throwsThrowable {

try {

this.init();

MethodProxy.FastClassInfofci=this.fastClassInfo;

returnfci.f1.invoke(fci.i1, obj, args);

} catch (InvocationTargetExceptionvar4) {

throwvar4.getTargetException();

} catch (IllegalArgumentExceptionvar5) {

if (this.fastClassInfo.i1<0) {

thrownewIllegalArgumentException("Protected method: "+this.sig1);

} else {

throwvar5;

}

}

}

publicObjectinvokeSuper(Objectobj, Object[] args) throwsThrowable {

try {

this.init();

MethodProxy.FastClassInfofci=this.fastClassInfo;

returnfci.f2.invoke(fci.i2, obj, args);

} catch (InvocationTargetExceptionvar4) {

throwvar4.getTargetException();

}

}

http://www.lryc.cn/news/3028.html

相关文章:

  • Idea超好用的管理工具ToolBox(附带idea工具)
  • Spring 中 ApplicationContext 和 BeanFactory 的区别
  • 情人节有哪些数码好物值得送礼?情人节实用性强的数码好物推荐
  • java中flatMap用法
  • 【MySQL Shell】8.9.2 InnoDB ClusterSet 集群中的不一致事务集(GTID集)
  • logstash毫秒时间戳转日期以及使用业务日志时间戳替换原始@timestamp
  • 【C语言】qsort——回调函数
  • 8年软件测试工程师经验感悟
  • 腾讯云安全组配置参考版
  • 代码覆盖率工具OpenCppCoverage在Windows上的使用
  • 代码随想录算法训练营第24天25天|● 77. 组合● 216.组合总和III ● 17.电话号码的字母组合
  • Python_pytorch
  • 【Java|golang】2335. 装满杯子需要的最短总时长
  • shell编程之sed
  • 安全寒假作业nginx反向代理+负载均衡上传webshell重难点+apache漏洞
  • day35|01背包问题、416. 分割等和子集
  • Linux内核启动(3,0.11版本)内核启动完成与进入内核main函数
  • 【2023】Prometheus-Alertmanager高可用集群
  • 2023-2-11 刷题情况
  • 2019_41 考研408
  • Linux账号与用户组
  • 有趣的Hack-A-Sat黑掉卫星挑战赛——定位卫星Jackson
  • JAVA集合专题3 —— vector + LinkedList + Set
  • Scout:一款功能强大的轻量级URL模糊测试与爬取工具
  • leaflet 解决marker呈现灰色边框的问题
  • MySQL JSON类型字段的查找与更新
  • element Ui树状图控件 spring boot Vue 实现角色授权功能
  • 已解决sc delete MongoDB卸载MongoDB拒绝访问。
  • python的opencv操作记录11——阈值分割
  • Python-项目实战--飞机大战-英雄登场(7)