当前位置:首页 > CN2资讯 > 正文内容

动态代理详解动态代理和静态代理

2天前CN2资讯

作者:泥粑

 

摘要

本文动态代理得意义、主要介绍动态代理得实现原理以及由动态代理引申出来的一些知识点。

 

插曲

最近在研究javamelody实现的原理,发现他对JDBC的监控就是通过动态代理实现的。由于之前对于动态代理只是大概知道怎么回事,没有细致的去研究,所以上网百度了一下。发现网上的东西要么注重原理而忽略应用场景导致空泛、要么注重场景而忽略原理、要么就是只有基于接口的动态代理而没有基于cglib的。因此这里本文尽量做到大而全。其实想总结一下的原因是公司进行代码review的时候,老大提出同一个类中一个方法调用本类其他方法,其他方法的事务不会生效,本质上我是持怀疑态度的。当时我是出于基于Cglib代理的角度考虑,而实际不会生效是基于动态代理的方式,采用cglib还是会生效,后面会讲到。本人作文比较推崇简约易懂的方式,尽量避免过于斯文的名词出现。

 

一、动态代理的意义

 首先明白一点,动态代理就是用来生成代理对象的。我们知道传统的代理模式,通常是先定义一个代理类,该代理类需要持有目标对象(也有叫被代理对象,我觉得都行吧)。假设我们有1000个不同的目标对象(这1000个对象不是同一个类),那么我们需要预先定义1000个代理类,这是我们不能容忍的。于是乎,动态代理就出现了,它本质上是生成一个外表上和目标对象一样的代理对象,然后当我们调用代理对象的方法的时候,实际上它在他的方法里面去调用了目标对象对应的同名方法。

 

二、动态代理设计的核心思想

其实不要把这些设计想得多么高尚,假如我是动态代理设计的作者,由动态代理的意义部分我们知道,我们就是要想尽一切办法,通过目标对象生成代理对象,然后让代理对象的方法调用作用到目标对象的方法调用。没错动态代理的核心思想就是这么简单。比如目标类为Person,Person有一个方法叫做purchase(),此方法用于购物。我们期望purchase()方法有代理类去做处理,比如在购物前记录下购买了哪些东西。我们知道在使用一个类之前,是需要创建一个对象的,我们就在创建的地方动手脚。所以你看到了JDK动态Proxy.newInstance()的方式,也领略过Spring的Enhancer.create()。个人比较喜欢cglib的优雅、干净、利落。吐槽一下JDK的InvocationHandler像极了恶心的中间商。下面是JDK动态代理UML示意图

三、JDK动态代理

1,原理

在了解动态代理之前,我们需要了解Java字节码。如果不熟悉Java字节码,你可以理解为通过代码动态生成一个.java文件,然后将其编译为class文件加载到内存中。接下来JDK中的动态代理要做的事情就是怎么去生成一个ProxyPerson字节码文件。其实它就是在生成字节码的时候,持有了InvocationHandler对象,然后去实现了ProxyPerson对应的接口。在该接口的所有实现方法中,只做了一件事情就是调用invocationHandler.invoke()方法。从代码层面来看如下所示:

  •  
public class ProxyPerson implements Purchase{

static{ Method method;// 接口的方法 Object[] args;// 接口参数 }
InvocationHandler handler;public ProxyPerson(InvocationHandler handler){ this.handler = handler;}

@overrdepublic purchage(){ this,handler.invoke(this,method,args); }
}

 

 那么上面这段代码是在什么时候生成的呢? 

Proxy.newProxyInstance()

在我们调用JDK上面的这个方法的时候,底层就会去生成一个ProxyPerson字节码。知道了原理我们来解答一下JDK动态代理为何只能基于接口代理而不能基于类呢?

1),受限于字节码的生成方式,JDK本身就是基于InvocationHandler去做的代理中转。我们看到代理对象的方法调用于目标对象的调用没有半毛球关系,调用目标对象是我们自己在invoke方法里面完成的。

2),受限于同名的方法只能被向上转型成功的对象调用。比如有两个类Boy与Girl,他们都实现了接口Purchase,如果我们先获取到Girl的purchase()方法method,我们通过method.invoke(new Boy())这样必定会报错。但是如果我们获取到Purchase接口purchase()方法method,我们通过method.invoke(new Boy())这样是ok的,因为new Boy()可以向上转型为Purchase。

2,应用

比如无论是传统的MVC模型还是DDD模型,都离不开Service。我们知道Service方法使用@Transactional是可以开启事务控制的。那么这种注解式事务是如何实现的呢?其实在工程启动的时候,我们就会有一个Bean的后置处理器去检查所有Bean一旦发现Bean的方法上有事务注解,他就通过Proxy.newInstance()去创建一个代理对象,将代理对象进行返回注入,而抛弃原本应该注入到容器的对象。所以我们看起来通过容器拿到的Service其实已经是代理对象了。在调用目标对象前,开启编程式事务即可。

 

四、cglib动态代理

 有了上面的知识,我们要有对于cglib而言只是在生成字节码上面动手脚的觉悟。下面直观感受与一下生成过程

  •  
public static void main(final String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Boy.class); enhancer.setCallback(new MethodInterceptor(){ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("proxy method "+ method.getName()); if(method.getAnnotation(Transactional.class)!=null){ System.out.println(method.getName()+"发现注解"); } return methodProxy.invokeSuper(o,args); } }); Boy proxy = (Boy) enhancer.create(); proxy.test(); } public static class Boy{ public void run(){ System.out.println("run..."); } @Transactional public void walk(){ System.out.println("walk..."); } @Transactional public void test(){ System.out.println("test..."); walk(); } }

可以看到cglib是基于继承的方式进行字节码动态生成。

它在子类的实现中,只是调用了注入的methodIntercptor.interceptor()方法。具体字节码实现细节,这里不在深究。我们在这里探讨一下,为什么cglib可以使同一个service方法中的其他带有事务注解的事务生效?因为基于继承的动态代理,本质发起上调用的代理对象可以向上转型为原本的目标对象,所以它可以直接通过代理对象去调目标对象方法。

 

    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/32111.html

    分享给朋友:

    “动态代理详解动态代理和静态代理” 的相关文章

    如何找到便宜域名?免费与低价域名注册全攻略

    知名免费二级域名介绍 在寻找免费域名时,有几个知名的选项值得考虑。eu.org 是一个长期存在的免费二级域名,支持DNSSEC,适合部署在Vercel等平台上。不过,它在中国某些地区可能会遇到访问限制,且申请时间不确定。US.KG 是另一个热门选择,可以接入Cloudflare,但需要有一个活跃的G...

    香港VPS推荐:选择适合您的虚拟服务器的最佳指南

    在如今的互联网时代,香港VPS逐渐成为了个人和企业的热门选择。香港VPS,简单来说,是一种虚拟专用服务器,位于香港的数据中心。它为用户提供云计算的强大能力,不论是网站托管、应用开发,还是数据存储,都能灵活应对需求。对于希望在亚太地区拓展业务的用户来说,这无疑是一个理想的解决方案。 香港VPS的多样化...

    腾讯云与Cloudflare的比较分析:助力企业数字化转型的最佳选择

    在数字化转型的浪潮下,越来越多的企业开始依赖云计算技术来推动业务发展。其中,腾讯云和Cloudflare作为行业的佼佼者,吸引了广泛的关注。腾讯云是腾讯集团倾力打造的云计算品牌,凭借强大的技术背景和丰富的产品线,逐渐成为全球知名的云计算服务商。它的基础设施覆盖全球五大洲,运营于58个可用区,能够为不...

    莱卡云:高性价比云服务器解决方案,助力企业发展

    莱卡云概述 莱卡云,这个名字你或许已经听说过。成立于2019年,它在云计算服务领域迅速崭露头角,成为一些企业的首选合作伙伴。凭借持有的多个《中华人民共和国增值电信业务经营许可证》,莱卡云有效地展开了多种多样的服务,充分满足了市场需求。与三大公有云的合作让它在竞争激烈的市场中站稳了脚跟,提供的解决方案...

    最新hudsonvalleyhost优惠码使用技巧与推荐2023

    hudsonvalleyhost 优惠码介绍 hudsonvalleyhost优惠码是一种可以帮助用户在使用hudsonvalleyhost服务时节省费用的代码。这种优惠码一般由字母和数字组成,用户在进行购买或订阅相关服务时输入该代码,就可以享受到折扣或特定的优惠。每个优惠码的有效期和适用范围可能不...

    黑五VPS促销: 省钱又省心的选择指南

    在这个数字化的时代,VPS(虚拟专用服务器)成为越来越多业务和个人的选择。VPS其实是一种独立的虚拟服务器,它通过物理服务器上的虚拟化技术进行资源分隔。因此,不同用户可以在同一台物理服务器上,拥有独立的操作空间。想象一下,你可以像使用传统的独立服务器一样,享受更高的性能和安全性,同时又不需要承担独立...