Java动态代理机制 代码示例demo

文章目录

  • JDK动态代理代码实现示例
    • 1.定义发送短信的接口
    • 2.实现发送短信的接口
    • 3.定义一个 JDK 动态代理类
    • 4.获取代理对象的工厂类
    • 5.实际使用
  • JDK 动态代理只能代理实现了接口的类
  • CGLIB动态代理代码实现示例
    • 1.实现一个使用阿里云发送短信的类
    • 2.自定义 MethodInterceptor(方法拦截器)
    • 3.获取代理类
    • 4.实际使用
  • JDK 动态代理和 CGLIB 动态代理对比
  • 参考资料

JDK动态代理代码实现示例

1.定义发送短信的接口

package dynamic_proxy;/*** 定义发送短信的接口*/
public interface SmsService {void send(String message);
}

2.实现发送短信的接口

package dynamic_proxy;/*** 实现发送短信的接口 的类*/
public class SmsServiceImpl implements SmsService{@Overridepublic void send(String message) {System.out.println("send message:" + message);}
}

这是一个 之后被代理的类。

3.定义一个 JDK 动态代理类

package dynamic_proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** 定义一个JDK动态代理类*/
public class DebugInvocationHandler implements InvocationHandler {private final Object target;public DebugInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//调用方法之前,我们可以添加自己的操作System.out.println("before method " + method.getName());Object result = method.invoke(target, args);//调用方法之后,我们同样可以添加自己的操作System.out.println("after method " + method.getName());return result;}
}

这个动态代理类将可以代理上面的 SmsServiceImpl 对象。

其必须实现 InvocationHandler 接口,重写 invoke 方法。
invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。

4.获取代理对象的工厂类

package dynamic_proxy;import java.lang.reflect.Proxy;public class JdkProxyFactory {public static Object getProxy(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new DebugInvocationHandler(target));}
}

使用该定义的静态方法可以声明一个代理对象。

这里使用了工厂模式。

工厂模式(Factory Pattern)是最常用的设计模式之一,它属于创建类型的设计模式。它提供了一种创建对象的最佳方式,在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过一个共同的接口来指向新创建的对象。

5.实际使用

package dynamic_proxy;public class Main {public static void main(String[] args) {SmsService smsService = (SmsService)JdkProxyFactory.getProxy(new SmsServiceImpl());smsService.send("java");}
}

运行结果为

before method send
send message:java
after method send

这里 SmsService 是一个接口名,众所周知,接口不能实例化。实际上,我们不能直接实例化一个接口,但可以通过实例化实现了接口的类来达到相同的效果。

如果我们打印出来System.out.println(JdkProxyFactory.getProxy(new SmsServiceImpl()).getClass());,会得到class com.sun.proxy.$Proxy0

JDK 动态代理只能代理实现了接口的类

在这里插入图片描述

在这里插入图片描述

具体来说,JDK动态代理是通过实现目标接口的方式来创建代理类的,代理类会实现目标接口中定义的所有方法,并在代理类中通过反射调用目标对象的方法,从而完成代理的功能。因此,如果目标对象没有实现任何接口,那么JDK动态代理就无法生成代理类。
如果要对一个没有实现任何接口的类进行代理,可以使用其他的代理技术,比如CGLIB,它可以生成一个子类来作为代理类,从而实现对目标对象的代理。

CGLIB动态代理代码实现示例

首先创建一个 maven 项目,添加依赖:

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

1.实现一个使用阿里云发送短信的类

package cglib_dynamic_proxy;public class AliSmsService {public void send(String message) {System.out.println("send message:" + message);}
}

2.自定义 MethodInterceptor(方法拦截器)

package cglib_dynamic_proxy;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** 自定义MethodInterceptor*/
public class DebugMethodInterceptor implements MethodInterceptor {/*** @param o           被代理的对象(需要增强的对象)* @param method      被拦截的方法(需要增强的方法)* @param args        方法入参* @param methodProxy 用于调用原始方法*/@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//调用方法之前,我们可以添加自己的操作System.out.println("before method " + method.getName());Object object = methodProxy.invokeSuper(o, args);//调用方法之后,我们同样可以添加自己的操作System.out.println("after method " + method.getName());return object;}}

3.获取代理类

package cglib_dynamic_proxy;import net.sf.cglib.proxy.Enhancer;public class CglibProxyFactory {public static Object getProxy(Class<?> clazz) {// 创建动态代理增强类Enhancer enhancer = new Enhancer();// 设置类加载器enhancer.setClassLoader(clazz.getClassLoader());// 设置被代理类enhancer.setSuperclass(clazz);// 设置方法拦截器enhancer.setCallback(new DebugMethodInterceptor());// 创建代理类return enhancer.create();}
}

4.实际使用

package cglib_dynamic_proxy;public class Main {public static void main(String[] args) {AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);aliSmsService.send("java");}
}

JDK 动态代理和 CGLIB 动态代理对比

  1. JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
  2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

参考资料

https://javaguide.cn/java/basis/proxy.html#_3-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86
面试官:为什么jdk动态代理只能代理接口实现类?
基于Jdk的动态代理为什么只能基于接口?

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

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

相关文章

Java——猫猫图鉴微信小程序(前后端分离版)

目录 一、开源项目 二、项目来源 三、使用框架 四、小程序功能 1、用户功能 2、管理员功能 五、使用docker快速部署 六、更新信息 审核说明 一、开源项目 猫咪信息点-ruoyi-cat: 1、一直想做点项目进行学习与练手&#xff0c;所以做了一个对自己来说可以完成的…

HCIP:rip综合实验

实验要求&#xff1a; 【R1-R2-R3-R4-R5运行RIPV2】 【R6-R7运行RIPV1】 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.加快网络收敛&#xff0c;减少路由条目数量&#xff0c;增…

Unity中URP下的添加雾效支持

文章目录 前言一、URP下Shader支持雾效的步骤1、添加雾效变体2、在Varying结构体中添加雾效因子3、在顶点着色器中&#xff0c;我们使用内置函数得到雾效因子4、在片元着色器中&#xff0c;把输出颜色 和 雾效因子混合输出 二、在Unity中打开雾效三、测试代码 前言 我们使用之…

ubuntu20部署Bringing-Old-Photos-Back-to-Life

环境准备&#xff1a; ubuntu20.04 Python 3.8.10 首先将微软的「Bringing-Old-Photos-Back-to-Life」库 clone 到本地&#xff1a; git clone https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life.git cd Face_Enhancement/models/networks/ git clone https:/…

NFC物联网智慧校园解决方案

近场通信(Near Field Communication&#xff0c;NFC)又称近距离无线通信&#xff0c;是一种短距离的高频无线通信技术&#xff0c;允许电子设备之间进行非接触式点对点数据传输交换数据。这个技术由免接触式射频识别(RFID)发展而来&#xff0c;并兼容 RFID&#xff0c;主要用于…

解决VNC连接Ubuntu服务器打开终端出现闪退情况

服务器环境 阿里云ECS服务器 操作系统&#xff1a;Ubuntu 20.0.4 如何使用VNC连接阿里云ECS服务器 1.阿里云官方指导&#xff1a;通过VNC搭建Ubuntu 18.04和20.04图形界面 2.新手入门ECS——ubuntu 20.04安装图形化界面和本地VNC连接 问题描述 使用VNC连接上新申请阿里云服…

github鉴权失败

问题&#xff1a; 如上图所示 git push 时发生了报错&#xff0c;鉴权失败&#xff1b; 解决方案 Settings->Developer settings->Personal access tokens->Generate new token。创建新的访问密钥&#xff0c;勾选repo栏&#xff0c;选择有效期&#xff0c;为密钥命…

鸿蒙HarmonyOS-图表应用

简介 随着移动应用的不断发展&#xff0c;数据可视化成为提高用户体验和数据交流的重要手段之一。在HarmonyOS应用开发中&#xff0c;一个强大而灵活的图表库是实现这一目标的关键。而MPChart就是这样一款图表库&#xff0c;它为开发者提供了丰富的功能和灵活性&#xff0c;使得…

【持续更新ing】uniapp+springboot实现个人备忘录系统【前后端分离】

目录 &#xff08;1&#xff09;项目可行性分析 &#xff08;2&#xff09;需求描述 &#xff08;3&#xff09;界面原型 &#xff08;4&#xff09;数据库设计 &#xff08;5&#xff09;后端工程 接下来我们使用uniappspringboot实现一个简单的前后端分离的小项目----个…

Spark作业的调度与执行流程

Apache Spark是一个分布式计算框架&#xff0c;用于处理大规模数据。了解Spark作业的调度与执行流程是构建高效分布式应用程序的关键。本文将深入探讨Spark作业的组成部分、调度过程以及执行流程&#xff0c;并提供丰富的示例代码来帮助大家更好地理解这些概念。 Spark作业的组…

[Angular] 笔记 16:模板驱动表单 - 选择框与选项

油管视频&#xff1a; Select & Option (Template Driven Forms) Select & Option 在 pokemon.ts 中新增 interface: export interface Pokemon {id: number;name: string;type: string;isCool: boolean;isStylish: boolean;acceptTerms: boolean; }// new interface…

前端图片适配不同屏幕方案

预备知识&#xff1a; 设备独立像素,以下图的iphone12 Pro为例&#xff0c;390*844表示的就是设备独立像素&#xff08;DIP&#xff09;,也可以理解为CSS像素 物理像素&#xff08;设备像素&#xff09;&#xff0c;就是屏幕的分辨率&#xff0c;显示屏就是由一个个物理像素…

QT/C++ 远程数据采集上位机+服务器

一、项目介绍&#xff1a; 远程数据采集与传输 课题要求:编写个基于TCP的网络数据获取与传输的应用程序; 该程序具备以下功能: 1)本地端程序够通过串口与下位机(单片机)进行通信&#xff0c;实现数据采集任务 2)本地端程序能将所获取下位机数据进行保存(如csv文本格式等); 3…

初识隧道代理HTTP:理解基础概念的重要性

嗨&#xff0c;小伙伴们&#xff01;如果你对网络世界充满好奇&#xff0c;那么这篇文章就是为你准备的。我们将一起踏上一段奇妙的旅程&#xff0c;探索一个叫做“隧道代理HTTP”的新领域。但在这之前&#xff0c;我们需要先穿上“基础概念”的防护服&#xff0c;以免被这个复…

MR实战:实现数据去重

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据文件1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、Map阶段实现&#xff08;1&#xff09;创建Maven项目&#xff08;2&#xff09;添加相关依赖…

一语道破爬虫,来揭开爬虫面纱

目录 一、爬虫&#xff08;网络蜘蛛(Spider)&#xff09; 1.1、是什么&#xff1a; 1.2、学习的原因 1.3、用在地方&#xff1a; 1.4、是否合法&#xff1a; 1.5、后果 案例&#xff1a; 二、应用领域 三、Robots协议 四、抓包 4.1、浏览器抓包 4.2、抓包工具 常见…

从0到1浅析Redis服务器反弹Shell那些事

文章目录 前言Redis服务1.1 特点与应用1.2 安装与使用1.3 语法和配置1.4 未授权访问 反弹Shell2.1 Web服务写入Webshell2.2 Linux定时任务反弹shell2.3 /etc/profile.d->反弹shell2.4 写入ssh公钥登录服务器2.5 利用Redis主从复制RCE2.6 SSRF漏洞组合拳->RCE 总结 前言 …

stm32中的i2c协议

stm32中I2C 文章目录 stm32中I2CI2C 协议简介I2C物理层协议层I2C基本读写过程 **通讯的起始和停止信号****数据有效性****地址及数据方向****响应** STM32的I2C特性及架构**STM32** **的** I2C外设简介STM32 的 I 2C 架构剖析通讯引脚 通讯过程主发送器主接收器 I2C初始化结构体…

【Linux】指令(本人使用比较少的)——笔记(持续更新)

文章目录 ps -axj&#xff1a;查看进程ps -aL&#xff1a;查看线程echo $?&#xff1a;查看最近程序的退出码jobs&#xff1a;查看后台运行的线程组fd 任务号&#xff1a;将后台任务提到前台bg 任务号&#xff1a;将暂停的后台程序重启netstat -nltp&#xff1a;查看服务及监听…

Unity Meta Quest 一体机开发(十二):【手势追踪】Poke 交互 - 用手指点击由 3D 物体制作的 UI 按钮

文章目录 &#x1f4d5;教程说明&#x1f4d5;给玩家配置 HandPokeInteractor&#x1f4d5;用 3D 物体制作可以被点击的 UI 按钮⭐搭建物体层级⭐给物体添加脚本⭐为脚本变量赋值 &#x1f4d5;模仿官方样例按钮的样式&#x1f4d5;在按钮上添加文字&#x1f4d5;修改按钮图片 …