手写动态代理--java

文章目录

  • 动态代理
    • 静态代理的写法
    • 动态代理基本写法
      • 静态化具体方法
    • 改成用JDK的InvocationHandler

动态代理

首先明确代理模式是一个类的方法不能直接使用,我们在客户端和目标对象之间创建一个中介,这个中介就是动态代理,例如租房者,中介和房东之间,租房者通过中介访问房东。
在程序执行的过程中创建代理类对象,在创建过程中利用反射机制来实现的。
即为在使用过程中利用JDK提供的方法才会创建对象。
动态代理的实现有两种:
JDK动态代理:使用jdk字节的类,利用反射实现的代理,反射包Java.lang.reflect包括三个类:Proxy,Method和InvocationHandler
Method:能够实现所有某个接口定义的方法,具体运行哪个具体类的方法,根据invoke传入的对象来决定的。
InvocationHandler:只有一个接口invoke() ,表示代理对象要执行的功能代码,你的代理类完成的功能就写在invoke() 方法中。
代理类完成的功能:调用目标方法,并且功能增强

静态代理的写法

被代理类接口

package proxy1;public interface Foo {void fo();int bar();
}

被代理类

package proxy1;public class target1 implements Foo{@Overridepublic void fo() {System.out.println("fo");}@Overridepublic int bar() {System.out.println("bar");return 1;}
}

代理类

package proxy1;public class $Proxy1 implements Foo{@Overridepublic void fo() {System.out.println("before fo");new target1().fo();}@Overridepublic int bar() {System.out.println("before bar");return new target1().bar();}
}

执行

package proxy1;public class Main1 {public static void main(String[] args) {Foo proxy = new $Proxy1();proxy.fo();proxy.bar();}
}

结果

before fo
fo
before bar
bar

动态代理基本写法

上面的代理类的方法都是写死的,如 new target1().fo(); return new target1().bar();
如果语句是需要动态的执行的话,那么就不好搞了,因此需要把这些动态语句,抽离出来。
这时候定义了InvocationHnadler接口,来表达这个被代理的类,通过h.invoke()方法来执行真实的类,就有了如下写法
我们通过反射的方式动态获取方法对象。
void invoke(Method method, Object[] args)
修改传入参数,一个是要执行的方法,一个是传入方法的参数
在代理中通过反射的方式来获取方法,并且将参数传进去
被代理接口

package proxy2;import java.lang.reflect.InvocationTargetException;public interface Foo {void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;int bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}

被代理的类

package proxy2;public class target1 implements Foo {@Overridepublic void fo() {System.out.println("fo");}@Overridepublic int bar() {System.out.println("bar");return 1;}
}

代理接口

package proxy2;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public interface InvocationHandler {Object invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}

代理类

package proxy2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {proxy2.InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Method method = Foo.class.getMethod("fo");h.invoke(method, null);}@Overridepublic int bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Method method = Foo.class.getMethod("bar");String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(method, str);}
}

具体执行

package proxy2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Main1 {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Foo proxy = new $Proxy1(new InvocationHandler() {@Overridepublic Object invoke(Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {System.out.println("before...");return method.invoke(new target1(), args);}});proxy.fo();System.out.println(proxy.bar());}
}

通过预先定义一个InvocationHandler h,h中传入具体的类的方法即可通过反射的方式实现方法的执行。
ps:这里需要补充一下lamda表达式的写法,在new $Proxy1时传入一个new InvocationHandler(),这是一个接口需要具体实现,于是就有了

{@Overridepublic Object invoke(Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {System.out.println("before...");return method.invoke(new target1(), args);}
}

实际上,这里有个隐形的实现类,不过被隐藏起来了,可以简化为lamda表达式

 Foo proxy = new $Proxy1((proxy1, method, objects) -> {System.out.println("before...");return method.invoke(new target1(), args);});

(proxy1, method, objects)就代表实现了接口的类

静态化具体方法

进一步的优化,将被代理类的方法进行静态化,这样可以加快执行速度

package proxy4;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() {try {h.invoke(this, fo, null);} catch (Throwable e) {e.printStackTrace();}}@Overridepublic int bar() {try {String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(this, bar , str);} catch (Throwable e) {e.printStackTrace();return -1;}}//静态化被代理方法static Method bar;static Method fo;static {try {bar = Foo.class.getMethod("bar");fo = Foo.class.getMethod("fo");} catch (NoSuchMethodException e) {e.printStackTrace();}}
}
package proxy3;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() {try {h.invoke(this, fo, null);} catch (InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}}@Overridepublic int bar() {try {String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(this, bar , str);} catch (InvocationTargetException | IllegalAccessException e) {e.printStackTrace();return -1;}}static Method bar;static Method fo;static {try {bar = Foo.class.getMethod("bar");fo = Foo.class.getMethod("fo");} catch (NoSuchMethodException e) {e.printStackTrace();}}
}

改成用JDK的InvocationHandler

在程序中自定义了InvocationHandler
如果删除这个接口,将引用的这个接口改变为import java.lang.reflect.InvocationHandler;实际上效果是一样的
被代理类:

package proxy4;public class target1 implements Foo {@Overridepublic void fo() {System.out.println("fo");}@Overridepublic int bar() {System.out.println("bar");return 1;}
}

代理类

package proxy4;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() {try {h.invoke(this, fo, null);} catch (Throwable e) {e.printStackTrace();}}@Overridepublic int bar() {try {String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(this, bar , str);} catch (Throwable e) {e.printStackTrace();return -1;}}static Method bar;static Method fo;static {try {bar = Foo.class.getMethod("bar");fo = Foo.class.getMethod("fo");} catch (NoSuchMethodException e) {e.printStackTrace();}}
}

测试代码

package proxy4;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;public class Main1 {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {Foo proxy = new $Proxy1((proxy1, method, objects) -> {System.out.println("before...");return method.invoke(new target1(), args);});proxy.fo();System.out.println(proxy.bar());System.out.println(proxy.getClass());System.in.read();}
}

实现InvacationHandler接口,重写invoke()方法,在invoke() 方法中增强类的方法

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

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

相关文章

在 Python 中使用装饰器decorator的 7 个层次

在 Python 中使用装饰器的 7 个层次(7 Levels of Using Decorators in Python) 文章目录 在 Python 中使用装饰器的 7 个层次(7 Levels of Using Decorators in Python)导言Level 0: 了解基本概念Basic Concepts和用法Usages什么是装饰器decorator?我们为什么需要装…

Python新年炫酷烟花秀代码

新年马上就要到来,烟花秀必须得安排上! Pygame 绘制烟花的基本原理 1,发射阶段:在这一阶段烟花的形状是线性向上,通过设定一组大小不同、颜色不同的点来模拟“向上发射” 的运动运动,运动过程中 5个点被赋…

硅像素传感器文献调研(三)

写在前面: 引言:也是先总结前人的研究结果,重点论述其不足之处。 和该方向联系不大,但还是有值得学习的地方。逻辑很清晰,易读性很好。 1991年—场板半阻层 使用场板和半电阻层的高压平面器件 0.摘要 提出了一种…

线程基础知识(三)

前言 之前两篇文章介绍了线程的基本概念和锁的基本知识,本文主要是学习同步机制,包括使用synchronized关键字、ReentrantLock等,了解锁的种类,死锁、竞争条件等并发编程中常见的问题。 关键字synchronized synchronied关键字可…

Oracle中varchar2和nvarchar2的区别

Oracle中的varchar2和nvarchar2都是可变长度的字符数据类型,这意味着它们能够根据实际存储的数据长度来动态调整占用的空间。但它们之间有以下主要区别: 1. 字符编码和存储: - VARCHAR2:存储的是字节字符串,对字符…

出现频率高达80%的软件测试常见面试题合集(内附详细答案)

最近看到网上流传着各种面试经验及面试题,往往都是一大堆技术题目贴上去,但是没有答案。 为此我业余时间整理了这份软件测试基础常见的面试题及详细答案,望各路大牛发现不对的地方不吝赐教,留言即可。 01 软件测试理论部分 1.1…

Java线程池执行流程及参数详解

线程池的定义分为以下几个部分&#xff1a; public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {...}我们分别来看…

leaflet学习笔记-初始化vue项目(一)

leaflet简介 Leaflet是一款开源的轻量级交互式地图可视化JavaScript库&#xff0c;能够满足大多数开发者的地图可视化需求&#xff0c;其最早的版本大小仅仅38 KB。Leaflet能够在主流的计算机或移动设备上高效运行&#xff0c;其功能可通过插件进行扩展&#xff0c;拥有易于使用…

金睛云华斩获ISC2023数字安全创新能力百强 五项殊荣!

昨日&#xff0c;由北京市通州区人民政府指导&#xff1b;中关村科技园区通州园管理委员会、ISC平台主办的ISC2023数字安全创新能力百强颁奖典礼在京举行。金睛云华以卓越的产品创新实力&#xff0c;一举斩获网络与流量安全、威胁检测与响应、AI驱动安全、创新力十强、年度十强…

【JavaEE】多线程(6) -- 定时器的使用及实现

目录 定时器是什么 标准库中的定时器的使用 实现定时器 定时器是什么 Java中的定时器是一种机制&#xff0c;用于在预定时间执行某个任务。它允许开发人员在指定的时间间隔内重复执行任务&#xff0c;或在指定的延迟之后执行任务。定时器是Java提供的一种方便的工具&#xf…

本地部署 text-generation-webui

本地部署 text-generation-webui 0. 背景1. text-generation-webui 介绍2. 克隆代码3. 创建虚拟环境4. 安装 pytorch5. 安装 CUDA 运行时库6. 安装依赖库7. 启动 Web UI8. 访问 Web UI9. OpenAI 兼容 API 0. 背景 一直喜欢用 FastChat 本地部署大语言模型&#xff0c;今天试一…

web3方向产品调研

每次互联网形态的改变&#xff0c;都会对世界产生很大的影响&#xff0c;上一次对社会产生重大影响的互联网形态&#xff08;Web2.0&#xff09;催生了一批改变人类生活和信息交互方式的企业。 目录 概述DAO是什么&#xff1f;为什么我们需要DAO? 金融服务金融桥接及周边服务D…

Spring AOP—深入动态代理 万字详解(通俗易懂)

目录 一、前言 二、动态代理快速入门 1.为什么需要动态代理&#xff1f; : 2.动态代理使用案例&#xff1a; 3.动态代理的灵活性 : 三、深入动态代理 1.需求 : 2.实现 : 2.1 接口和实现类 2.2 提供代理对象的类 2.3 测试类 3.引出AOP : 四、总结 一、前言 第四节内容&…

Mybatis行为配置之Ⅱ—结果相关配置项说明

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL 文章目录 专栏精选引言摘要正文autoMappingBehaviorautoMappingU…

基于遗传算法的航线规划

MATLAB2016b可以正常运行 基于遗传算法的无人机航线规划资源-CSDN文库

代码随想录算法训练营Day13 |104.二叉树的最大深度、559.N叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

LeetCode 104 二叉树的最大深度 本题思想&#xff1a;用后序遍历的思想&#xff0c;先判断出左子树的深度&#xff0c;然后在右子树的深度。最后取大的那一个再加上根节点。就是最大深度。 class Solution {public int maxDepth(TreeNode root) {if(root null){return 0;}int…

(13)Linux 进程的优先级、进程的切换以及环境变量等

前言&#xff1a;我们先讲解进程的优先级。然后讲解进程的切换&#xff0c;最后我们讲解环境变量&#xff0c;并且做一个 "让自己的可执行程序不带路径也能执行"的实践&#xff0c;讲解环境变量的到如何删除&#xff0c;最后再讲几个常见的环境变量。 一、进程优先级…

跟着LearnOpenGL学习10--基础光照

文章目录 一、前言二、环境光照三、漫反射光照3.1、法向量3.2、计算漫反射光照3.3、全部代码3.4、法线矩阵 四、镜面光照4.1、全部代码 一、前言 现实世界的光照是极其复杂的&#xff0c;而且会受到诸多因素的影响&#xff0c;这是我们有限的计算能力所无法模拟的。 因此Open…

使用Rust发送邮件

SMTP协议与MIME协议 SMTP&#xff08;简单邮件传输协议,Simple Mail Transfer Protocol&#xff09;是一种用于发送和接收电子邮件的互联网标准通信协议。它定义了电子邮件服务器如何相互发送、接收和中继邮件。SMTP 通常用于发送邮件&#xff0c;而邮件的接收通常由 POP&#…

响应式绑定<a-textarea>的内容

项目中的 <a-textarea>组件需要自动填入下方数据表的物品名称数量单位&#xff0c;效果如下&#xff1a; 尝试 <a-textarea>{{插值}}</a-textarea>&#xff0c;实现不了&#xff0c;问ai得知需要使用v-decorator 指令的initialValue 属性 问&#xff1a; 如何…