简单实现动态代理(Proxy)

2023-10-28

前言

最近学习了Jdk的动态代理,然后自己也简单的手写了一个。

思路

  1. 根据代理的接口,生成对应的Java代码文件
  2. 将生成的Java文件编译成class文件
  3. 利用URLClassLoader加载class到Jvm中,利用反射在new出这个对象。

业务代理接口

package com.michael.pl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public interface InvocationHandler {

    Object invock(Object object, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;

}

需要代理的接口

package com.michael.pl.service;

public interface LogService {

    void sayHi() throws Exception;

    Object out(String text) throws Exception;

}
package com.michael.pl.service.impl;

import com.michael.pl.service.LogService;

public class LogServiceImpl implements LogService {

    @Override
    public void sayHi() {

        System.out.println("hello");

    }

    @Override
    public Object out(String text) {
        return text;
    }
}

实现动态代理的核心类

package com.michael.pl;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class MyProxy {

    public static final String TABLE = "    ";
    public static final String ENTER = "\n";

    public static Object newProxyInstance(ClassLoader classLoader, Class<?> interfaceClass,
        InvocationHandler invocationHandler) throws Exception {

        String javaCode = buiderJavaFile(interfaceClass);
//        System.out.println(javaCode);

        String className = interfaceClass.getName().substring(interfaceClass.getName().lastIndexOf(".") + 1);
        File file = new File("\\com\\sun\\proxy\\$" + className + ".java");

        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }

        if (!file.exists()) {
            file.createNewFile();
        }

        // 把拼好的Java文件写到硬盘当中
        FileWriter fw = new FileWriter(file);
        fw.write(javaCode);
        fw.close();

        // 把写到硬盘的Java文件编译成class
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(file);
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();


        // 吧编译上的class文件加载到JVM中
        URL[] urls = new URL[]{new URL("file:G:\\\\")};


        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class clazz = urlClassLoader.loadClass("com.sun.proxy.$" + className);

        Constructor constructor = clazz.getConstructor(InvocationHandler.class);
        return constructor.newInstance(invocationHandler);
    }

    /**
     * 生成对应的Java代码
     */
    public static String buiderJavaFile(Class<?> interfaceClass) throws Exception {

        String className = interfaceClass.getName().substring(interfaceClass.getName().lastIndexOf(".") + 1);

        StringBuilder content = new StringBuilder();
        content.append("package com.sun.proxy;" + ENTER );
        content.append("import java.lang.reflect.Method;" + ENTER);
        content.append("import java.lang.Exception;" + ENTER);
        content.append("import com.michael.pl.InvocationHandler;" + ENTER);
        content.append("public class $" + className + " implements " + interfaceClass.getName() + "{" + ENTER);

        content.append(TABLE + "private InvocationHandler i;" + ENTER);

        // 添加构造方法
        content.append(forTable(1) + " public $" + className + "(InvocationHandler i){"+ENTER);
        content.append(forTable(1) + "this.i = i;" + ENTER);
        content.append(forTable(1) + "}" + ENTER);


        Method[] methods = interfaceClass.getMethods();
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];

            String returnType = method.getReturnType().getName();

            int j = 0;
            String paramContent = "";
            String callParam = "";
            String paramClass = "";
            for (Class<?> paramType : method.getParameterTypes()) {
                paramContent += paramType.getName() + " args" + j + ",";
                callParam += "args" + j + ",";
                paramClass += paramType.getName() + ".class,";
                j++;
            }
            if (paramContent.length()>0) {
                paramContent = paramContent.substring(0, paramContent.length() - 1);
                callParam = "new Object[]{" + callParam.substring(0, callParam.length() - 1) + "}";
                paramClass = paramClass.substring(0, paramClass.length() - 1);
            }

            if (callParam.length() == 0) {
                callParam = "null";
            }


            String exceptionContent = "";
            for (Class<?> exceptionType : method.getExceptionTypes()) {
                exceptionContent += exceptionType.getName() + ",";
            }
            if (exceptionContent.length()>0) {
                exceptionContent = "throws " + exceptionContent.substring(0, exceptionContent.length() - 1);
            }

            content.append(forTable(1) + "public " + returnType + " " + method.getName() + "(" + paramContent + ") "
                + exceptionContent + "  {" + ENTER);
            content.append(forTable(2) + "Method declaredMethod = Class.forName(\"" + interfaceClass.getName()
                + "\").getDeclaredMethod(\"" + method.getName() + "\"" + (paramClass.length() == 0 ? "" :
                "," + paramClass) + ");" + ENTER);
            if (!"void".equals(method.getReturnType().getName())) {
                content.append(forTable(2) + "return (" + returnType + ")");
            }
            content.append("i.invock(this,declaredMethod,"+callParam+");" + ENTER);

            content.append(forTable(1) + "}" + ENTER);
        }


        content.append("}" + ENTER);

        return content.toString();

    }


    private static String forTable(int i) {
        String str = "";
        for (int j = 0; j < i; j++) {
            str += TABLE;
        }
        return str;

    }


}
View Code

测试类

package com.michael.pl;

import com.michael.pl.service.LogService;
import com.michael.pl.service.impl.LogServiceImpl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestDemo {

    public static void main(String[] args) throws Exception {


        // 被代理的对象
        LogServiceImpl logServiceImpl = new LogServiceImpl();

        LogService logService = (LogService)MyProxy.newProxyInstance(TestDemo.class.getClassLoader(), LogService.class,
            new InvocationHandler() {

                @Override
                public Object invock(Object object, Method method, Object[] argss)
                    throws InvocationTargetException, IllegalAccessException {
                    System.out.println(" 代理类容!!");
                    return method.invoke(logServiceImpl, argss);
                }

            });

        logService.sayHi();
        logService.out("你好");



    }

}

 代码下载地址:https://files.cnblogs.com/files/MichaelPL/MyProxy.zip

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简单实现动态代理(Proxy) 的相关文章

  • 任务“:app:dexDebug”执行失败

    我目前正在处理我的项目 我决定将我的 Android Studio 更新到新版本 但在我导入项目后 它显示如下错误 Information Gradle tasks app assembleDebug app preBuild UP TO
  • Java 创建浮雕(红/蓝图像)

    我正在编写一个 Java 游戏引擎 http victoryengine org http victoryengine org 并且我一直在尝试生成具有深度的 3D 图像 您可以使用那些红色 蓝色眼镜看到 我正在使用 Java2D 进行图形
  • JTree 节点不会被直观地选择

    不知何故 我无法为我的 JTree 节点启用 选择突出显示 我正在我的项目中使用自定义单元格渲染器 这很可能导致此问题 这是完整的渲染器类代码 protected class ProfessionTreeCellRenderer exten
  • Java 泛型/类型调度问题

    考虑以下程序 import java util List import java util ArrayList public class TypeTest public static class TypeTestA extends Type
  • Java 重写 hashCode() 得到 StackOverflowError

    所以我不太熟悉重写 hashCode 并且我似乎在 hashCode 方法中以某种方式进行了一些无限递归 这是我的场景 我有一个 DuplicateCache 类 它是一个缓存对象 用于检查系统中的重复对象 我有一个静态内部类 Duplic
  • Java:从元素创建 DOM 元素,而不是文档

    如您所知 在 Java 中创建 Dom 元素的正确方法是执行以下操作 import org w3c dom Document import org w3c dom Element Document d Element e e d creat
  • Android - 除了普通 SSL 证书之外还验证自签名证书

    我有一个通过 SSL 调用 Web 服务的 Android 应用程序 在生产中 我们将拥有由受信任的 CA 签名的普通 SSL 证书 但是 我们需要能够支持自签名证书 由我们自己的 CA 签名 我已经成功实施了接受自签名证书的建议解决方案
  • Spring Stomp over Websocket:流式传输大文件

    我的SockJs客户端在网页中 发送帧大小为16K的消息 消息大小限制决定了我可以传输的文件的最大大小 以下是我在文档中找到的内容 Configure the maximum size for an incoming sub protoco
  • 如何将 XMP XML 块序列化为现有的 JPEG 图像?

    我有许多 JPEG 图像 其中包含损坏的 XMP XML 块 我可以轻松修复这些块 但我不确定如何将 固定 数据写回图像文件 我目前正在使用 JAVA 但我愿意接受任何能让这项任务变得容易的事情 这是目标关于 XMP XML 的另一个问题
  • Java替换特定字符

    这是我在这个网站上的第一个问题 所以我会尽量不要成为一个十足的菜鸟 我目前正在用java 创建刽子手游戏 所以我问你的问题是我们是否被赋予了 幽灵 这个词 并将 Ghost 替换为 hiddenWord ghost length for i
  • Java:VM 如何在 32 位处理器上处理 64 位“long”

    JVM 如何在 32 位处理器上处理 64 位的原始 long 在多核 32 位机器上可以并行利用多个核心吗 64 位操作在 32 位机器上慢了多少 它可能使用多个核心来运行不同的线程 但不会并行使用它们进行 64 位计算 64 位长基本上
  • tomcat 过滤所有 web 应用程序

    问题 我想对所有网络应用程序进行过滤 我创建了一个过滤器来监视对 apache tomcat 服务器的请求 举例来说 它称为 MyFilter 我在 netbeans 中创建了它 它创建了 2 个独立的目录 webpages contain
  • 不兼容的类型:在 java netbeans 中对象无法转换为 String

    我试图在我的项目中使用对象数组 但出现错误 incompatible types Object cannot be converted to String 在这一行 ST1 new String emt1 emt2 emt3 emt4 现在
  • 使用 Java 从 S3 上的文件在 S3 上创建 zip 文件

    我在 S3 上有很多文件 需要对其进行压缩 然后通过 S3 提供压缩文件 目前 我将它们从流压缩到本地文件 然后再次上传该文件 这会占用大量磁盘空间 因为每个文件大约有 3 10MB 而且我必须压缩多达 100 000 个文件 所以一个 z
  • Spock模拟inputStream导致无限循环

    我有一个代码 gridFSFile inputStream bytes 当我尝试这样测试时 given def inputStream Mock InputStream def gridFSDBFile Mock GridFSDBFile
  • Path2D 上的鼠标指针检测

    我构建了一个Path2D http docs oracle com javase 7 docs api java awt geom Path2D html表示由直线组成的未闭合形状 我希望能够检测何时单击鼠标并且鼠标指针靠近路径 在几个像素
  • 从一个文本文件中获取数据并将其移动到新的文本文件

    我有一个文件 里面有数据 在我的主要方法中 我读入文件并关闭文件 我调用另一种方法 在原始文件的同一文件夹内创建一个新文件 所以现在我有两个文件 原始文件和通过我调用的方法生成的文件 我需要另一种方法 从原始文件中获取数据并将其写入创建的新
  • Java中获取集合的幂集

    的幂集为 1 2 3 is 2 3 2 3 1 2 1 3 1 2 3 1 假设我有一个Set在爪哇中 Set
  • Java中有类似分支/跳转表的东西吗?

    Java有类似分支表或跳转表的东西吗 分支表或跳转表是 根据维基百科 http en wikipedia org wiki Branch table 用于描述使用分支指令表将程序控制 分支 转移到程序的另一部分 或可能已动态加载的不同程序
  • java中如何找到class文件的包

    我正在编写一个使用 class 文件的 java 程序 我希望能够读取文件系统上的 class 文件 使用 InputStream 并确定它所在的包 该 class 文件可能不在一个好的包目录结构中 它可能位于某个随机位置 我怎样才能做到这

随机推荐

  • git创建远程分支

    一 创建远程分支 git clone https XXXXX git branch 查看当前本地分支 只有master git checkout b query vip 在本地创建 query vip 分支 git push origin
  • 计算工作日时长

    计算工作日时长 工具类说明 mapWorkDay 自定义节假日 主要针对国内每年制定发布的国内假日及补班时间 一般维护到数据库 每年更新一次 针对特殊日期标识 工作日 节假日 dateUnit 时间计算精确单位 天 小时 分钟 秒等 代码
  • python-中文闲聊的GPT2模型首次使用笔记

    项目地址 https github com yangjianxin1 GPT2 chitchat 运行项目 下载bin模型地址 https github com yangjianxin1 GPT2 chitchat model share
  • Chisel 教程翻译 之 第三章 构建过程和测试 of 《Digital Design with Chisel》

    3 Build Process and Testing 21 3 1 Building your Project with sbt 21 3 1 1 Source Organization 21 3 1 2 Running sbt 23 3
  • win10系统定位服务器地址,win10系统定位服务器地址

    win10系统定位服务器地址 内容精选 换一换 将NFS文件系统挂载到Windows IIS服务器时 报错路径格式不支持 挂载失败 IIS Web服务器的物理路径错误 根据可能原因进行故障排查 如图1为文件系统的挂载地址 如图2需填写的物理
  • TV二次侧为什么必须接地?

    TV二次侧为什么必须接地 答 TV二次侧接地属保护接地 为防止一 二次侧绝缘损坏击穿 高电压串到二次侧来 对人身和设备造成危险 所以二次侧必须接地
  • xcode安装pod

    Mac终端 1 升级mac本地ruby环境 终端中输入 gem update system 如果出现权限问题 请在终端内输入sudo gem update system 2 更换镜像 移除现有镜像 gem sources remove ht
  • perl实现verilog ifdef所在域的判断

    功能描述 perl实现verilog ifdef所在域的判断 1 emacs verilog mode用于实例化很方便 但是常见项目 均存在大量ifdef的预编译命令 而emacs verilog mode不支持ifdef 2 手动实例化
  • mmdetect2d训练自己的数据集(一)—— labelme数据处理

    前言 近期在学习mmdetect 总体来说mmlab这个框架感觉上手难度还挺大的 自己也是结合b站各位up主 up主名称 我是土堆 OneShotLove 比飞鸟贵重的多 HKL 以及知乎mmlab官方边看边学 真的是保姆级教程 强烈推荐
  • C++学习(四七二)android保存文件

    1 保存到 data user 0 com flx testfilestorage files try mode参数注意下 这里使用的Context MODE PRIVATE FileOutputStream fileOutputStrea
  • 【单片机毕业设计】【mcuclub-jj-045】基于单片机的红外遥控器的设计

    最近设计了一个项目基于单片机的红外遥控器系统 与大家分享一下 一 基本介绍 项目名 红外遥控器 项目编号 mcuclub jj 045 单片机类型 STC89C52 STM32F103C8T6 具体功能 1 从机利用4 4键盘通过红外发射管
  • level7 项目实战:基于Linux的Flappy bird游戏开发

    目录 1 Flappy bird项目介绍 项目介绍 功能总结 项目框图 Ncurses库安装 Ncurses库函数介绍 2 信号机制详解 相关函数介绍 3 项目实现 1 Flappy bird项目介绍 项目介绍 目标 借助Ncurses库
  • Java--ArrayList遍历的三种方法

    Java遍历主要有以下几种 分别是利用for循环 或者for each 把链表变为数组进行遍历 利用迭代 IntIterator 遍历 下面我们分别进行学习 For循环 import java util ArrayList import j
  • ONVIF测试工具 ONVIF Device Test Tool的使用

    ONVIF测试工具 ONVIF Device Test Tool的使用 双击 打开软件 选择当前网络 点击 Discover Devices 进行搜索 可以看到搜索到一个设备
  • 使用OpenWRT配置SFTP远程文件传输,安全高效的文件传输方法

    文章目录 前言 1 openssh sftp server 安装 2 安装cpolar工具 3 配置SFTP远程访问 4 固定远程连接地址 前言 本次教程我们将在OpenWRT上安装SFTP服务 并结合cpolar内网穿透 创建安全隧道映射
  • ip代理

    为什么会出现IP被封 网站为了防止被爬取 会有反爬机制 对于同一个IP地址的大量同类型的访问 会封锁IP 过一段时间后 才能继续访问 如何应对IP被封的问题 有几种套路 修改请求头 模拟浏览器 而不是代码去直接访问 去访问 采用代理IP并轮
  • PC中自带计算器使用说明

    Backspace 删除当前显示数字的最后一位 CE 清除显示数字 C 清除当前的计算 MC 清除内存中的所有数字 MR 重调用存内存中的数字 该数字保留在内存中 MS 将显示数字保存在内存中 M 将显示的数字与内存中已有的任何数字相加 但
  • 记录一次线上OOM问题排查处理过程

    背景 项目为docker部署的springboot单体项目 非前后端分离 前端文件是集成在项目的类路径的resources路径下的 项目使用ruoyi vue版本做为开发原始代码 系统目前没什么用 主要是客户分公司在基础数据模块录入数据比较
  • 1001 害死人不偿命的(3n+1)猜想 PAT乙级真题 C++

    1001 害死人不偿命的 3n 1 猜想 卡拉兹 Callatz 猜想 对任何一个正整数 n 如果它是偶数 那么把它砍掉一半 如果它是奇数 那么把 3n 1 砍掉一半 这样一直反复砍下去 最后一定在某一步得到 n 1 卡拉兹在 1950 年
  • 简单实现动态代理(Proxy)

    前言 最近学习了Jdk的动态代理 然后自己也简单的手写了一个 思路 根据代理的接口 生成对应的Java代码文件 将生成的Java文件编译成class文件 利用URLClassLoader加载class到Jvm中 利用反射在new出这个对象