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

动态代理 java原生 vs Cglib

代理模式

代理模式的定义:

Provide a surrogate or placeholder for another object to control access to it.

即为对象提供一个代理以控制对其的访问。

代理模式应用最多的场景是不侵入被代理对象的前提下,对被代理对象进行功能增强。

不侵入被代理对象却能增强被代理对象的功能,这一NB特性使得代理模式无论是日常编码、亦或是各类框架中被广泛应用。

最NB的应用场景是AOP,动态代理是AOP最基础的底层技术逻辑,不论是哪一家的AOP框架,其核心其实都是通过动态代理实现的。

动态代理的概念

代理分为静态代理和动态代理,静态代理指的是在代码编写的过程中实现代理模式,相对非常简单。

动态代理指的是在运行时生成代理对象的字节码、从而实现在运行过程中改变或者增强被代理对象的行为。由于动态代理不需要在编码阶段通过代码方式写死代理模式的实现,因此具有很强的灵活性。

JAVA中应用最广的动态代理实现方式有两种,一种是JDK原生动态代理实现方式,另一种是通过Cglib实现。

这两种方式的根本区别就是,JDK原生动态代理基于接口实现,运行时生成的代理对象通过实现该接口生成。所以,要求被代理对象也必须实现该接口。

Cglib是基于继承实现,所以,对被代理对象是否继承接口没有要求。运行时生成的代理对象是被代理对象的子类的对象。因此,要求被代理对象的被代理方法不能是final的(final方法不能被扩展)、被代理对象必须有无参构造器。

这两个要求如果不满足的话,Cglig有不同的处理:
final方法不能被代理,即使你创建了Cglib代理对象、通过代理对象调用了被代理对象的final方法,但最终执行的仍然是被代理对象的方法,也就是说,该方法无法被代理对象增强。所以,使用Cglib调用代理对象的final方法后,运行并不会报错,只不过是不能达到增强方法的目的。

但是如果被代理对象没有无参构造器的话,Cglib代理对象运行时会抛出异常,程序无法运行。没有无参构造器当然不是说没有显式定义无参构造器,因为如果某一个类没有任何显式定义任何构造器的话,这个类是有无参构造器的,但是如果定义了有参数构造器、但是却没有显式定义无参构造器,这种情况下才是真正意义的没有无参构造器。

实现案例

我们就用最简单的HelloWorld举例,假设场景为:HelloWorld有三个方法:hello、check、check1,方法非常简单,就是打印一句话。

要实现的目标是:通过动态代理实现在调用hello方法前、调用后分别打印一句话,hello方法调用check方法,check方法调用check1。通过在被代理对象内部调用check和chekc1方法来观察一下,check和check1方法是否能被代理增强。

JDK原生动态代理

使用JDK原生动态代理需要以下元素:

  1. 被代理对象(必须实现接口)。
  2. 实现了InvocationHandler接口的动态代理回调处理器。
  3. 代理对象。

被代理对象:
由于JDK原生动态代理要求被代理对象必须实现接口,所以,首先定义接口:

public interface HelloWorldInterface {void hello();void check();void check1();
}

被代理对象的实现类:

public class HelloWroldImp implements HelloWorldInterface {@Overridepublic void hello() {System.out.println("Hello from ProxyJDK1Imp");check();}@Overridepublic void check(){System.out.println("I am check");check1();}@Overridepublic void check1(){System.out.println("I am last function check1");}

回调处理器
回调处理器是针对代理对象而言的,代理对象执行被代理对象的接口方法时,会调用回调对象的invoke方法。实际动态代理的方法增强功能完全是依赖于回调处理器的invoke方法实现的:回调对象中的target就是被代理对象,通过invoke方法最终调用到被代理对象的原始方法,而在invoke方法中我们可以实现增强功能。本例需求只是在调用前后打印日志,所以在调用被代理对象方法前后分别输出日志即可。

public class JDKProxyHandle implements InvocationHandler {private Object target;public JDKProxyHandle(Object target){this.target=target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before invoke " + method.getName());method.invoke(target,args);System.out.println("after invoke "+ method.getName());return null;}
}

代理对象
通过Proxy的静态方法.newProxyInstance创建代理对象,首先需要创建被代理对象实例,并将该实例作为参数传入回调对象的初始化方法创建回调对象。之后创建代理对象:

public static void main(String[] args) {HelloWorldInterface helloWorldInterface=new HelloWroldImp();InvocationHandler handler=new JDKProxyHandle(helloWorldInterface);HelloWorldInterface proxyHelloWorld=(HelloWorldInterface) Proxy.newProxyInstance(helloWorldInterface.getClass().getClassLoader(),helloWorldInterface.getClass().getInterfaces(),handler);
}

测试
执行代理对象的hello方法:

public static void main(String[] args) {HelloWorldInterface helloWorldInterface=new HelloWroldImp();InvocationHandler handler=new JDKProxyHandle(helloWorldInterface);HelloWorldInterface proxyHelloWorld=(HelloWorldInterface) Proxy.newProxyInstance(helloWorldInterface.getClass().getClassLoader(),helloWorldInterface.getClass().getInterfaces(),handler);proxyHelloWorld.hello();proxyHelloWorld.check();}

输出

before invoke hello
Hello from ProxyJDK1Imp
I am check
I am last function check1
after invoke hello
before invoke check
I am check
I am last function check1
after invoke check

从测试结果可以得到如下结论:

  1. 被代理对象的hello()方法被增强。
  2. hello()方法内部调用的check()方法,check()方法内部调用check1()。check()和check1()方法均不能再次被增强。
  3. 单独通过代理对象调用check()方法,check()方法被增强

所以,JDK原生动态代理的增强功能在代理对象内部调用中不能被传递!!!

Cglib动态代理

使用Cglib动态代理需要以下元素:

  1. 被代理对象(不需要实现接口)。
  2. 实现了MethodInterceptor接口的拦截器。
  3. 代理对象。

被代理对象:
Cglib动态代理底层通过扩展生成被代理对象的子类实现,所以,被代理对象不需要实现接口,仍然使用上例的HelloWorldImp作为被代理对象。
MethodInterceptor拦截器
拦截器的作用同JDK原生动态代理的回调器,拦截器内部需要持有被代理对象target,需要实现回调方法intercept,并在intercept方法内部实现增强。

public class CglibInterceptor implements MethodInterceptor {private Object target;public CglibInterceptor(Object target){this.target=target;}@Overridepublic Object intercept(Object o, Method method,Object[] objects,MethodProxy methodProxy) throws Throwable {System.out.println("This is cglib Proxy before"+method.getName());methodProxy.invokeSuper(o,objects);System.out.println("This is cglib Proxy after " + method.getName());return null;}
}

代理对象
以下创建代理对象的方法可以在client端实现,也可以在拦截器内部实现,也可以专门创建一个Proxy工厂获取。其中最重要的就是这个Cglib的enhancer,他是实现Cglib动态代理的关键。enhancer有两个最重要的成员变量,一个是superclass,另一个是回调对象。很容易理解,superclass是被代理对象的class对象,用来创建代理对象,回调对象就是上面我们创建的拦截器,用来在执行代理对象方法的时候进行回调、实现增强。

//创建代理public Object createProxy(){//创建核心类Enhancer enhancer = new Enhancer();//设置父类enhancer.setSuperclass( target.getClass());//设置回调CglibInterceptor cli=new CglibInterceptor();enhancer.setCallback(cli);// 生成代理Object proxy = enhancer.create();return proxy;}

测试

public static void main(String[] args) {HelloWroldImp hwi = new HelloWroldImp();CglibInterceptor  cgi=new CglibInterceptor(hwi);HelloWroldImp proxyHwi=(HelloWroldImp)cgi.createProxy();proxyHwi.hello();}

输出

This is cglib Proxy beforehello
Hello from ProxyJDK1Imp
This is cglib Proxy beforecheck
I am check
This is cglib Proxy beforecheck1
I am last function check1
This is cglib Proxy after check1
This is cglib Proxy after check
This is cglib Proxy after hello

从测试结果可以得到如下结论:

  1. 被代理对象的hello()方法被增强。
  2. hello()方法内部调用的check()方法,check()方法内部调用check1()。均被再次增强。

所以,Cglib动态代理的增强功能在代理对象内部调用中可以被传递增强!!!

http://www.dtcms.com/a/414431.html

相关文章:

  • MQTT协议基础知识速成(智能家居项目)
  • 北京网站建设认知网络推广公司服务内容
  • 爬虫疑难问题解决方案整理
  • 如何制作PDF文件目录?
  • 左右翻网站模版网页美工设计教程
  • 牛客小白月赛121
  • 深入理解目标文件:从ELF格式到链接核心
  • Java系列知识之 ~ Spring 与 Spring Boot 常用注解对比说明
  • 郫县建设局网站wordpress如何运行
  • LeetCode 114.二叉树展开为链表
  • 机器人中的电机与扭矩入门
  • 站长平台如何推广自己的网站怎么做网页版网站
  • 深入理解BFC:解决margin折叠和浮动高度塌陷的利器
  • Spec Kit - 规范驱动开发工具包
  • 具有价值的常州做网站免费进销存软件哪个简单好用
  • creo二次开发seo职位信息
  • Windows 10 环境下 Redis 编译与运行指南
  • 【编号206】房地产统计年鉴2002~2023
  • 某大型广告公司实习感受
  • 【Day 68】Zabbix-自动监控-Web检测-分布式监控
  • 企业网站建设公司公司网站开发客户挖掘
  • 天拓四方集团IOT平台:赋能电气设备制造商数智化转型新引擎
  • 【STM32项目开源】基于STM32的智能鱼缸养殖系统
  • 【小迪安全v2023】学习笔记集合 --- 持续更新
  • Django - DRF
  • Python全方位处理XML指南:解析、修改与重写实战
  • LabVIEW实现B样条曲线拟合
  • 门户网站系统建设招标文件中国建设教育协会网站培训中心
  • 常熟网站网站建设在线教育自助网站建设平台
  • 【Linux】深入探索多线程编程:从互斥锁到高性能线程池实战