【Java动态代理如何实现】

在这里插入图片描述

✅Java动态代理如何实现

    • ✅JDK动态代理和Cglib动态代理的区别
  • ✅拓展知识仓
    • ✅静态代理和动态代理的区别
    • ✅动态代理的用途
    • ✅Spring AOP的实现方式
    • 📑JDK 动态代理的代码段
    • 📑Cglib动态代理的代码块
  • ✅注意事项:


在Java中,实现动态代理有两种方式:


1 . JDK动态代理 : Java.lang.reflect 包中的Proxy类和 InvocationHandler 接口提供了生成动态代理类的能力。


2 . Cglib动态代理 : Cglib (Code Generation Library) 是一个第三方代码生成类库,运行时在内存中动态生成一个了类对象从而实现对目标对象功能的扩展。


用一张图片看一下什么是动态代理(概念):

在这里插入图片描述


✅JDK动态代理和Cglib动态代理的区别


JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类就可以使用CGLIB实现。


Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的 interception (拦截)。


Calib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。


所以,使用JDK动态代理的对象必须实现一个或多个接口:而使用cgib代理的对象则无需实现接口,达到代理类无侵入。


✅拓展知识仓

✅静态代理和动态代理的区别


最大的区别就是静态代理是编译期确定的,但是动态代理却是运行期确定的。




同时,使用静态代理模式需要程序员手写很多代码,这个过程是比较浪费时间和精力的。一旦需要代理的类中方法比较多,或者需要同时代理多个对象的时候,这无疑会增加很大的复杂度。


反射是动态代理的实现方式之一。

✅动态代理的用途


Java的动态代理,在日常开发中可能并不经常使用,但是并不代表他不重要。Java的动态代理的最主要的用途就是应用在各种框架中。因为使用动态代理可以很方便的运行期生成代理类,通过代理类可以做很多事情,比如AOP,比如过滤器、拦截器等。


在我们平时使用的框架中,像 servlet 的 filter 、包括 spring 提供的 aop 以及 struts2 的拦截器都使用了动态代理功能。我们日常看到的mybatis分页插件,以及日志拦截、事务拦截、权限拦截这些几乎全部由动态代理的身影。


✅Spring AOP的实现方式


Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。


JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler 接 和 Proxy 类。


如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。


CGLIB (Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。


📑JDK 动态代理的代码段


public class UserServiceImpl implements UserService {@Overridepublic void add() {// TODO Auto-generated method stubSystem,out,println("--------------------add----------------------");}
}public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {super();this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {PerformanceMonior.begin(target.getClass().getlame( )+"+method.getlame());//System.out .println("-----------------begin “+method.getName()+"---------);Object result = method.invoke(target, args);//System.out.println("--------------end "+method.getName( )+"-----);PerformanceMonior.end();return result;}public Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this);}
}public static void main(string[] args) {UserService seryice = new UserServiceImpl();MyInvocationHandler handler = new MyInvocationHandler(service);UserService proxy = (UserService) handler.getProxy();proxy .add();
}

📑Cglib动态代理的代码块


public class UserServiceImpl implements UserService {@Overridepublic void add() {//TODO Auto-generated method stubSystem.out,println("--------------------add----------------------");}}public class CglibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class clazz) {//设置需要创建子类的类enhancer.setSuperclass(clazz);enhancer.setCallback(this);//通过字节码技术动态创建子类实例return enhancer.create();}//实现MethodInterceptor接口方法public Object intercept(Object obj,Method method, Object[] args,MethodProxy proxy) throws Throwable {System.out.println("前置代理");//通过代理类调用父类中的方法Object result = proxy.invokeSuper(obj, args);System.out.printIn("后置代理");return result;}
}public class DoCGLib {public static void main(String[] args) {CglibProxy proxy = new CglibProxy();//通过生成子类的方式创建代理类UsenServiceImpl proxyImp = (UsenServiceImpl)proxy.getProxy(UserServiceImpl.class);proxyImp.add():}
}

✅注意事项:

JDK动态代理只能用于接口,不能用于类。

  • 动态代理只对方法调用有效,对字段访问和赋值无效
  • 如果目标对象抛出了异常,那么这个异常会被代理对象抛出,而不是在调用invoke方法时抛出

与CGLIB等其他代理技术的比较:


  • CGLIB:它是一个强大的高性能的代码生成库,可以扩展JAVA类和实现JAVA接口。它主要应用于高级OOP设计和应用,如AOP实现、缓存框架、事务管理等。
  • 两者的选择:如果你的目标是基于现有类的行为进行拦截或修改,可以使用CGLIB;如果目标是基于接口进行拦截或修改,那么应该使用JDK动态代理。

💡思考:


除了JDK动态代理和CGLIB,还有其他一些常用的代理技术,如Spring AOP、AspectJ等。这些技术提供了更高级的特性,如支持方法级别的拦截、支持运行时和编译时切面等。


// 导入java.lang.reflect包中的InvocationHandler和Proxy类  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  // 定义一个接口Hello  
interface Hello {  // 定义接口方法sayHello,没有实现  void sayHello();  
}  // 定义一个实现Hello接口的类HelloImpl  
class HelloImpl implements Hello {  // 实现接口方法sayHello  public void sayHello() {  System.out.println("Hello, world!");  }  
}  // 定义一个实现InvocationHandler接口的类DynamicProxyHandler  
class DynamicProxyHandler implements InvocationHandler {  // 私有成员变量obj,用来保存目标对象的引用  private Object obj;  // 构造方法,传入目标对象作为参数,赋值给obj成员变量  public DynamicProxyHandler(Object obj) { this.obj = obj; }  // 实现InvocationHandler接口的方法invoke,传入代理对象、方法、参数数组,返回值类型为Object  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {  // 在目标方法执行前输出"Before method call"  System.out.println("Before method call");  // 调用目标方法,传入参数args,返回值赋值给result变量  Object result = m.invoke(obj, args);   // 在目标方法执行后输出"After method call"  System.out.println("After method call");  // 返回目标方法的返回值或者异常(如果目标方法抛出了异常)  return result;    }  
}  public class DynamicProxyExample {  public static void main(String[] args) {  // 创建一个HelloImpl类的实例作为目标对象,并实现Hello接口的sayHello方法  Hello hello = new HelloImpl();   // 创建一个DynamicProxyHandler类的实例作为InvocationHandler,传入目标对象实例作为参数传入构造器中  InvocationHandler handler = new DynamicProxyHandler(hello);   /** 使用Proxy类的静态方法newProxyInstance创建代理对象实例,传入目标对象的类加载器、目标对象的接口数组以及* InvocationHandler实例作为参数传入构造器中。返回的是代理对象实例,类型为目标对象的接口类型。* 创建的代理对象实例可以直接像目标对象实例一样使用,只不过它实现了所有接口中的方法。* 所有这些方法的调用最终会调用到我们提供的InvocationHandler实例中对应的invoke方法中去处理。* 这样我们就能够在不修改原有代码的基础上为某个对象提供额外行为操作,* 也就是实现了在运行时动态扩展了某个类的行为功能操作,即实现了AOP的功能。* 这样就达到了在不修改原有代码的基础上扩展了某个类的行为功能操作的目的。* 比如可以在目标对象方法执行前后添加额外的逻辑操作处理。此处因为动态代理的目标是Hello接口,所以没有错误。*/ Hello proxy = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), new Class[] { Hello.class }, handler); }   
}

最后总结一下JDK动态代理的思想:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/299468.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dbeaver如何连接Oceanbase?

Dbeaver & Oceanbase 一、新增驱动二、连接数据库 一、新增驱动 1、新建驱动 点击数据库 -> 驱动管理器 -> 新建 2、设置驱动 驱动名称可随意填写注意驱动类型要是Generichost:port填写实际的host和port 库中新增下载的oceanbase驱动jar包 二、连接数据库 1、找…

c 语言,指针,指针的指针

c 语言,指针,指针的指针 指针就是指向变量地址的东西。 比如: 定义了一个 int 变量 p,值为 1 。定义了 int 指针 pInt 指向了变量 p, 它的名字前面有个 * ,此时 pInt 就是 p 的地址,当前面加…

多继承与多重继承

多继承与多重继承 实验介绍 多继承与多重继承虽然只相差一个字,但是却是两个不同的概念。实验首先是要区分多继承与多重继承,其次是要学习多继承与多重继承的使用方式。 知识点 多继承与多重继承概念继承构造函数多继承与多重继承概念 多继承与多重继承可以从字面上理解。…

pytorch 踩坑

pytorch 踩坑 在pytorch中,如果你定义了没用的组件,同样也会影响你的模型(我也不知道从哪里影响的),看一个例子 def _make_layer(self, block, planes, blocks, stride1, dilateFalse):norm_layer self._norm_layer#downsample Noneprevio…

取证练习(一)PC+手机,服务器未完

链接:https://pan.baidu.com/s/1KlkPwzWm7dNO2iRGoTsE7Q?pwdxyxy 提取码:xyxy –来自百度网盘超级会员V3的分享 每道题5分,共计200 一、请检查窝点中的手机检材,回答以下问题 1、 该OPPO手机的IMEI是: A. 8603700…

Linux操作系统——进程(三) 进程优先级

进程优先级 首先呢,我们知道一个进程呢(或者也可以叫做一个任务),它呢有时候要在CPU的运行队列中排队,要么有时候阻塞的时候呢又要在设备的等待队列中排队,其实我们排队的本质就是:确认优先级。…

MY FILE SERVER: 1

下载地址 https://download.vulnhub.com/myfileserver/My_file_server_1.ova 首先我们需要发现ip 我的kali是59.162所以167就是靶机的 然后我们拿nmap扫一下端口 nmap -sV -p- 192.168.59.167 扫完发现有七个端口开放 按照习惯先看80 没看到有啥有用信息,用nikto扫一下 nik…

java数据结构与算法刷题-----LeetCode633. 平方数之和

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 思路一:双指针 可以使用双指针,不断从两个方向匹配…

科学分配销售资源,CRM系统来帮您

大家好,今天小编为大家带来的是CRM使用技巧。我们知道CRM管理系统可以用来划分商机,但你知道如何借助CRM系统科学分配销售资源吗?通过阈值功能可以平衡销售人员工作量,最大化利用客户资源,全面提升转化效率。协助销售团…

PyAV 使用浅谈

背景: PyAV是一个用于音频和视频处理的Python库,它提供了一个简单而强大的接口,用于解码、编码、处理和分析各种音频和视频格式。PyAV基于FFmpeg多媒体框架,它本质上是FFmpeg 的Python绑定,因此可以利用FFmpeg的功能来…

Git 分布式版本控制系统(序章1)

第一章 Git 分布式版本控制系统 为什么学Git? 某些企业面试需要掌握Git,同时,也方便管理自己的Qt项目。 一、Git 客户端下载(Windows) 下载地址 https://gitee.com/all-about-git#git-%E5%A4%A7%E5%85%A8 二、Git 的特点 分支…

asp.net mvc 重定向问题的解决方式

前端ajax发起请求,在后端接口中重定向,结果报错,无法跳转 Ajax实际上是通过XMLHttpRequest来向服务器发送异步请求的,从服务器获取数据,然后使用JS来更新页面,这也就是常说的局部刷新实现方式,所…