首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

动态代理(一)

啦放假回家啦!

可是开心的同时也不要忘记学习哦

今天我们就来看一看关于动态代理的那些事儿

动态代理

动态代理是一种强大的功能,它可以在运行时动态的创建一个类,实现一个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为,广泛应用于各种系统程序中、框架和库中,比如:Spring、Hibernate、MyBatis、Guice等。

动态代理是面向切面编程的基础,切面的例子有日志、性能监控、权限检查、数据库事务,它们在程序中得到很多地方都会用到,代码都差不多,但与某个具体的业务逻辑的也不太密切,如果在每个用到的地方都写,代码会很荣誉,也难以维护,AOP将这些切面与主题逻辑相分离,代码简单优雅的多。

■ ■■■■

静态代理

代理是一个比较通用的词,作为一个软件设计模式,基本概念和日常生活中的代理类似,代理背后一般至少有一个实际的对象,代理的外部功能和实际对象一般是一样的,用户和代理打交道,不直接接触实际的代理对象,虽然和外部的实际对象一样,但是代理有他存在的价值,比如:

节省成本比较高的实际对象的创建开销,按需延迟加载,创建代理时并不真正创建实际的对象,而只是保存实际 对象的地址,在需要时再加载或者创建。

执行权限检查,代理检查权限后,在调用设计对象。

屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上,调用本地代理时,本地代理请求其他的服务器。

看一个简单的例子:

输出结果:

代理和实际对象一般有相同的接口,在这个接口,共同的接口是IService,实际对象是RealService,代理

TraceProxy。TraceProxy内部有一个IService的成员变量,对于方法sayhello的调用,它转发非了实际对象,在调用前后输出了一写跟踪调试信息。

适配器和装饰器模式与代理模式有点类似,它们背后都有一个别的对象,都是通过组合的方式指向该对象,不同之处在于,适配器是提供了一个不一样的新接口,装饰器是对原有的接口起到了"装饰"的作用,可能是增加了新接口,修改了原有的行为等,代理一般不改变接口。不过,我们并不是想强调它们的差别,可以将它们看做代理变体,统一看待。

在上面的例子中,我们想达到的目的是在实际对象的方法调用前后加一些调试语句,为了在不修改原类的情况下达到这个目的,我们在代码中加入创建了一个代理类TraceProxy,它的代码是在写程序时固定的,所以称之为静态代理。

输出跟踪调试信息是一个通用的需求,可以想象,如果每个类都小,而又不希望修改类的定义,我们需要为每个类 创建代理,实现所有接口,这个工作就太繁琐了如果在有其他的切面需求呢,整个工作可能又要重来一遍。这时就需要动态代理了,主要有两种实现方式,jdk动态代理和第三方库cglib动态代理。

■ ■■■■

JDK动态代理

它有三个参数:

1.loader表示类加载器,例子使用和IService一样的类加载器。

2.interfaces表示代理类要实现的接口列表,是一个数组,元素的类只能是接口,不能是普通的类,例子中 只有一个IService。

newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中某个接口类型,这里我们强制转换为IService类型,需要注意的是,它不能强制转换为某个类型,比如:RealService,即使它代理的对象类型为RealService。如果真的那样做了,即使在编写代码时不会报错,但是会在运行时抛出ClassCastException:

实际上只要将生成的代理类反编译之后,就会发现Proxy#newProxyInstance生成的代理类继承了Proxy类,实现了IService接口,所以直接将生成的代理类强转为IService接口的实现类是不正确的,即使代理类代理了IService接口的实现类。

SimpleInvocaionHandler实现了InvocationHanlder,它的构造方法接受一个参数realObj表示被代理的对 象,invoke方法处理所有的接口,他有三个参数:

1.proxy表示代理对象的本身,需要注意的是,它不是被代理的对象,这个参数一般用处不大。

2.method表示正在被调用的方法

3.args表示方法的参数

在SimpleInvocationHandler的invoke实现中,我们调用了method的invoke方法,传递了实际对象的realObj作为参数,达到了调用实际对象方法的目的,在调用任何方法的前后,我们输出了跟踪调试语句。 需要注意的是,不能proxy作为参数传递给method.invoke,比如:

Object result = method.invoke(proxy,args);

上面的语句会出现死循环,因为proxy表示当前代理对象,这么调用又会调用到SimpleInvocationHandler的invoke方法,如下:

未完待续……

我们明天再见呦

(^з^)-☆

程序员食堂

用了这么久了,

还没关注公众号吗。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180720G14SOG00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券