Java动态代理

所有interface类型的变量总是通过某个实例向上转型并赋值给接口类型变量的:

CharSequence cs = new StringBuilder();
//父类(CharSequence)   指向 子类(StringBuilder),且没有强转符号 ====> 向上转型。

有没有可能不编写实现类,直接在运行期创建某个interface的实例呢?

这是可能的,因为Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例。

什么叫运行期动态创建?听起来好像很复杂。所谓动态代理,是和静态相对应的。我们来看静态代码怎么写

定义接口:

public interface Hello {void morning(String name);
}

编写实现类:

public class HelloWorld implements Hello {public void morning(String name) {System.out.println("Good morning, " + name);}
}

创建实例,转型为接口并调用:

Hello hello = new HelloWorld();
hello.morning("Bob");

这种方式就是我们通常编写代码的方式。

还有一种方式是动态代码,我们仍然先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码JDK提供的动态创建接口对象的方式,就叫动态代理。

一个最简单的动态代理实现如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {public static void main(String[] args) {InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(method);if (method.getName().equals("morning")) {System.out.println("Good morning, " + args[0]);}return null;}};Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), // 传入ClassLoadernew Class[] { Hello.class }, // 传入要实现的接口handler); // 传入处理调用方法的InvocationHandlerhello.morning("Bob");}
}interface Hello {void morning(String name);
}

 在运行期动态创建一个interface实例的方法如下:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。

动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,把上面的动态代理改写为静态实现类大概长这样:

public class HelloDynamicProxy implements Hello {InvocationHandler handler;public HelloDynamicProxy(InvocationHandler handler) {this.handler = handler;}public void morning(String name) {handler.invoke(this,Hello.class.getMethod("morning", String.class),new Object[] { name });}
}

其实就是JVM帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码),并不存在可以直接实例化接口的黑魔法。

  • Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例;
  • 动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的。

 趣味理解 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;public class DynamicProxy {public static void main(String[] args) {// 小韭菜学生类Student ordinaryStudents = new OrdinaryStudents();ordinaryStudents.eat();ordinaryStudents.write();// 现在有一位特殊的学生,他是区长的儿子,我们自然要对他额外照顾,要给他加一下功能。// 一种思路是定义一个类:区长的儿子类,他继承自学生类,但世上儿子千千万,有区长的儿子,也有市长的儿子,更有省长的儿子,不能把他们挨个定义出来,// 现在就可以使用动态代理机制,动态的给区长的儿子加上功能,以后碰到市长、省长的儿子也同样处理。// InvocationHandler作用就是,当代理对象的原本方法被调用的时候,会重定向到一个方法,// 这个方法就是InvocationHandler里面定义的内容,同时会替代原本方法的结果返回。// InvocationHandler接收三个参数:proxy,代理后的实例对象。 method,对象被调用方法。args,调用时的参数。InvocationHandler handler = (proxy, method, handlerArgs) -> {// 从定义eat方法。if ("eat".equals(method.getName())) {System.out.println("我可以吃香喝辣!");return null;}// 从定义write方法。if ("write".equals(method.getName())) {System.out.println("我的作文题目是《我的区长父亲》。");// 调用普通学生类的write方法,流程还是要走的,还是要交一篇作文上去,不能太明目张胆。method.invoke(ordinaryStudents, handlerArgs);System.out.println("我的作文拿了区作文竞赛一等奖!so easy!");return null;}return null;};// 对这个实例对象代理生成一个代理对象。// 被代理后生成的对象,是通过People接口的字节码增强方式创建的类而构造出来的。它是一个临时构造的实现类的对象。// loder和interfaces基本就是决定了这个类到底是个怎么样的类。而h是InvocationHandler,决定了这个代理类到底是多了什么功能.// 通过这些接口和类加载器,拿到这个代理类class。然后通过反射的技术复制拿到代理类的构造函数,// 最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。// 最终实现可以在运行的时候才切入改变类的方法,而不需要预先定义它。Student sonOfDistrict = (Student) Proxy.newProxyInstance(ordinaryStudents.getClass().getClassLoader(), ordinaryStudents.getClass().getInterfaces(), handler);sonOfDistrict.eat();sonOfDistrict.write();}
}/*** 学生接口,能跑,能吃,能写作文。*/
interface Student {void eat();void run();void write();
}
/*** 小韭菜,能跑,能吃,能写作文。*/
class OrdinaryStudents implements Student {@Overridepublic void eat() {System.out.println("我在吃饭!");}@Overridepublic void run() {System.out.println("我在跑步!");}@Overridepublic void write() {System.out.println("我在写作文!");}
}
//最终输出:我在吃饭!我在写作文!我可以吃香喝辣!我的作文题目是《我的区长父亲》。我在写作文!我的作文拿了区作文竞赛一等奖!so easy!

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

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

相关文章

扫盲:常用NoSQL数据库

前言 关系型数据库产品很多,如 MySQL、Oracle、Microsoft SQL Sever 等,但它们的基本模型都是关系型数据模型。 非关系型数据库又称为:NoSQL ,没有统一的模型,而且是非关系型的。 常见的 NoSQL 数据库包括键值数据库、…

构建前端之光:JavaScript插件的研发艺术

前言 在前端开发的宇宙中,星星是网页,而照亮这个宇宙的,是我们前端开发者手中的JavaScript插件。插件就像乐高积木,可以将我们的代码块组装成复杂而精美的页面。本文将引导你走进JavaScript插件的世界,探讨如何开发、…

python unitest自动化框架

以下举一个最简单的unitest实例,包含备注,自己拉取代码运行一次就知道原理了 import unittest import osclass TestSample(unittest.TestCase):classmethoddef setUpClass(cls) -> None:print(整个测试类只执行一次)def setUp(self) -> None:prin…

const {}解构赋值

定义:ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。 解构赋值的基本规则:只要等号右边不是对象或数组,就先将其转换为对象。由于undefi…

Prometheus+grafana安装配置

Prometheus安装配置 Prometheus下载地址 官方地址:Download | Prometheus 可根据系统版本下载想要的安装包,复制链接地址 wget https://github.com/prometheus/prometheus/releases/download/v2.33.3/prometheus-2.33.3.linux-amd64.tar.gzwg 解压pr…

系列五、Java操作RocketMQ简单消息之同步消息

一、概述 同步消息的特征是消息发出后会有一个返回值,即RocketMQ服务器收到消息后的一个确认,这种方式非常安全,但是性能上却没有那么高,而且在集群模式下,也是要等到所有的从机都复制了消息以后才会返回,适…

游戏服务器成DDoS最大攻击重灾区

游戏产业的迅猛发展也让游戏产业成为被黑客攻击的重灾区。什么原因让游戏行业成为DDoS的攻击重点。总结有如下原因和主要手段: 1.游戏行业的攻击成本较低,攻防成本1:N。随着DDoS攻击的打法越来越复杂,攻击点更是越来越多&#xff…

php 字符串格式化绕过SQL注入

0x00 前言 CTF 加解密合集CTF Web合集网络安全知识库 本篇介绍php 字符串格式化绕过SQL注入相关知识 0x01 正文 php中的sprintf函数中,如果出现%1$/就会识别为 而在addslashes函数中,会给特殊字符加上一个\ 那么如果输入的是%1$ 经过addslashes处理…

【链表OJ 10】环形链表Ⅱ(求入环节点)

前言: 💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥 ✨✨刷题专栏:http://t.csdn.cn/UlvTc ⛳⛳本篇内容:力扣上链表OJ题目 目录 leetcode142. 环形链表 II 1.问题描述 2.代码思路 3.问题分析 leetcode142. 环形链…

04、添加 com.fasterxml.jackson.dataformat -- jackson-dataformat-xml 依赖报错

Correct the classpath of your application so that it contains a single, compatible version of com.fasterxml.jackson.dataformat.xml.XmlMapper 解决: 改用其他版本,我没写版本号,springboot自己默认的是 2.11.4 版本 成功启动项目…

Vulnhub: Ragnar Lothbrok: 1靶机

kali:192.168.111.111 靶机:192.168.111.226 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.226 作者提示修改hosts文件 目录爆破 gobuster dir -u http://armbjorn -w /usr/share/wordlists/dirbuster/directory-l…

Java自定义异常

Java标准库定义的常用异常包括&#xff1a; 当我们在代码中需要抛出异常时&#xff0c;尽量使用JDK已定义的异常类型。例如&#xff0c;参数检查不合法&#xff0c;应该抛出IllegalArgumentException&#xff1a; static void process1(int age) {if (age < 0) {throw new…

事务的特性

事务具有四个特性&#xff0c;这四个特性通常被称为ACID特性&#xff0c;它们是&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务是一个原子操作单元&#xff0c;其对数据的修改要么全部执行&#xff0c;要么全不执行。如果一个事务在执行过程中出错&am…

mysql中的with

概念 WITH 子句是 MySQL 中的一种 SQL 结构&#xff0c;又称为 Common Table Expression (CTE)。它在不影响原有 SQL 语句的情况下&#xff0c;允许开发人员临时创建一个内存中的结果集&#xff0c;然后对其进行操作。 作用 WITH 子句的主要用途是创建一个暂时的结果集&…

2023高教社杯数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

响应式图片与 CSS image-set

响应式图片 前置知识 art direction problem光栅图像与矢量图像 raster image and vector images img 能否担此重任 sizessrcset实际看一看 picture: img 的好姐妹 source实际看一看 CSS image-set 语法兼容性 其他注意事项 响应式图片 图片在网页中占据了 超过 60% 的浏览带…

ABP中的ConcurrencyStamp的自动化管理

在ABP中&#xff0c;你可以使得Entity直接继承接口 IHasConcurrencyStamp 然后再EF中的XXXDbContextModelCreatingExtensions中的ConfigureByConvention会看到如下代码 public static void TryConfigureConcurrencyStamp(this EntityTypeBuilder b){if (b.Metadata.ClrType.Is…

基于微信小程序的汽车租赁系统的设计与实现ljx7y

汽车租赁系统&#xff0c;主要包括管理员、用户二个权限角色&#xff0c;对于用户角色不同&#xff0c;所使用的功能模块相应不同。本文从管理员、用户的功能要求出发&#xff0c;汽车租赁系统系统中的功能模块主要是实现管理员后端&#xff1b;首页、个人中心、汽车品牌管理、…

题目:2621.睡眠函数

​​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;2621. 睡眠函数 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 按要求返回 Promise 对象&#xff0c;并在该 Promise 对象中休眠即可。 解题代码&#xff1a; /*** param {number} milli…

SAP_ABAP_OLE_EXCEL批导案例

SAP ABAP顾问能力模型梳理_企业数字化建设者的博客-CSDN博客SAP Abap顾问能力模型https://blog.csdn.net/java_zhong1990/article/details/132469977 一、OLE_EXCEL批导 1.1 下载按钮 1.2 选择EXCEL上传&#xff0c;解析EXCLE数据&#xff0c; Call屏幕。 1.3 实现效果 1.4…