idea插件开发之一起来开发个打印方法入参和返回值的插件吧!

news2025/1/12 1:03:08

写在前面

源码 。
在开发过程中为了调试代码我们就可能就需要知道某个方法入参的值是什么,或者是返回值是什么。此时,我们的解决办法一般都是debug,但是debug的效率说实话其实是不高的,特别是不断的调试,不断的debug。所以为了解决(😅,彻底解决不用debug,不太可能,但肯定能在一定程度上缓解吧)这个痛点问题,我们就来尝试开发一个idea插件吧。

1:编写agent程序

通过bytebuddy来实现字节码插桩,源码如下:

public class MyPreAgent {
    // 如果有该方法则优先调用这个有两个参数的方法
    public static void premain(String agentArgs, Instrumentation inst) {

        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
            return builder
//                    .method(ElementMatchers.named("executeInternal")) // 拦截任意方法
                    .method(ElementMatchers.named("m1").or(ElementMatchers.named("m2"))) // 拦截任意方法
                    .intercept(MethodDelegation.to(MonitorMethod.class)); // 委托
        };

        new AgentBuilder
                .Default()
//                .type(ElementMatchers.nameStartsWith("com.mysql.cj.jdbc.ClientPreparedStatement"))
                .type(ElementMatchers.nameStartsWith("com.dahuyou.agent.test.MyCls"))
                .transform(transformer)
                .installOn(inst);
    }

    // 如果是没有上面的方法则调用这个一个参数的方法
    public static void premain(String agentArgs) {
    }

}

在代码中我们设置了满足什么规则才拦截,这里是固定写死的拦截类com.dahuyou.agent.test.MyCls的方法m1和m2,当然可以随意改,或者是通过某种配置的方式做成活的,上述的类MonitorMethod即为插装的代码,源码如下:

public class MonitorMethod {

    @RuntimeType
    public static Object intercept(@This Object obj, @Origin Method method, @SuperCall Callable<?> callable, @AllArguments Object... args) throws Exception {
        System.out.println("MonitorMethod.intercept...");
        long start = System.currentTimeMillis();
        Object resultObj = null;
        try {
            resultObj = callable.call();
            return resultObj;
        } finally {
            System.out.println("方法名称:" + method.getName() + "入参个数:" + method.getParameterCount());
            for (int i = 0; i < method.getParameterCount(); i++) {
                System.out.println("入参 Idx:" + (i + 1) + " 类型:" + method.getParameterTypes()[i].getName() + " 内容:" + args[i]);
            }
            System.out.println("出参类型:" + method.getReturnType().getName());
            System.out.println("出参结果:" + resultObj);
            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
        }
    }

}

接着我们就可以生成agent的jar包来测试了,如下:
在这里插入图片描述
然后创建一个满足agent匹配规则的类:

public class MyCls {

    public String m1(String name) {
        System.out.println("m1方法执行了啊: " + name);
        this.m2(30, "一个啊神秘的地方");
        return name;
    }

    private int m2(int age, String addr) {
        System.out.println("m2方法执行了啊: " + age);
        return ++age;
    }
}

测试类:

public class MyPreAgentTest {

    public static void main(String[] args) {
        String name = "张三";
        new MyCls().m1(name);
    }
}

然后配置VM option添加-javaagent,如下:
在这里插入图片描述
最后运行测试:
在这里插入图片描述

接着就可以来继续开发插件了。

2:编写idea插件

要想让agent工作,必须通过-javaagent添加到java运行命令中,idea提供了java.programPatcher的扩展可以让我们实现这个需求,具体是继承抽象类JavaProgramPatcher,然后修改其中的入参RunProfile:

/**
 * 向Java cmd中动态添加-javaagent:xxx
 */
public class JavaCmdEditor extends JavaProgramPatcher {
    @Override
    public void patchJavaParameters(Executor executor, RunProfile configuration, JavaParameters javaParameters) {
        String agentCoreJarPath = PluginUtil.getAgentCoreJarPath();
        RunConfiguration runConfiguration = (RunConfiguration) configuration;
        ParametersList vmParametersList = javaParameters.getVMParametersList();
        // 向运行参数中增加-javaagent:/path/to/the-show-sql-agent.jar
        vmParametersList.addParametersString("-javaagent:" + agentCoreJarPath);
        vmParametersList.addNotEmptyProperty("com.dahuyou.the-probe-plugin", runConfiguration.getProject().getLocationHash());

    }
}

这样还不行,为了让idea加载运行,还需要配置到plugin.xml中的java.ProgramPatcher中:

<extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
    <java.programPatcher implementation="com.dahuyou.probe.plugin.JavaCmdEditor"/>
</extensions>

然后就可以来测试了:
在这里插入图片描述
运行:
在这里插入图片描述
到这里还有一个问题需要解决,即插装的类和方法是固定写死的,接下来我们开发一个UI来允许配置想要拦截的类和方法,就基本全了。

3:插桩代码支持配置

首先开发一个action,在file中增加一个按钮入口:

public class AgentSettingAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        Messages.showInfoMessage("即将跳转到agent设置页面", "提示");
        // 弹出一个设置(implements Configurable)页面(extends JPanel)
        ShowSettingsUtil.getInstance().editConfigurable(e.getProject(), new AgentSettingUI());
    }
}

这里AgentSettingUI是需要我们自定义的页面,继承了Japnel,并实现了Configurable接口:

public class AgentSettingUI extends JPanel implements Configurable {}

效果如下:
在这里插入图片描述
点击ok会调用apply方法,在该方法中会将用户设置的信息写到本地文件中:
在这里插入图片描述
接着还需要改造agent程序,动态的从文件中读取数据来进行拦截配置:

public static void premain(String agentArgs, Instrumentation inst) {

    String agentSettingFilePath = "D:/agentsetting.txt";
    String packagePrefix = "";
    List<String> methodList = null;
    try {
        BufferedReader br = new BufferedReader(new FileReader(agentSettingFilePath));
        String agentSettingInfo = br.readLine();
        // com.dahuyou3343#tttmyFn1,myFn2
        System.out.println("agentSettingInfo: " + agentSettingInfo);
        String[] split = agentSettingInfo.split("#");
        packagePrefix = split[0];
        methodList = Arrays.asList(split[1].split(","));
        System.out.println("packagePrefix: " + packagePrefix);
        System.out.println("methodList111: " + methodList);
    } catch (Exception e) {
        e.printStackTrace();
    }
    List<String> finalMethodList = methodList;
    AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
        ElementMatcher.Junction<NamedElement> named = ElementMatchers.named(finalMethodList.get(0));
        for (int i = 1; i < finalMethodList.size(); i++) {
            named = named.or(ElementMatchers.named(finalMethodList.get(i)));
        }
        return builder
                .method(named) // 拦截任意方法
                .intercept(MethodDelegation.to(MonitorMethod.class)); // 委托
    };

    new AgentBuilder
            .Default()
            .type(ElementMatchers.nameStartsWith(packagePrefix))
            .transform(transformer)
            .installOn(inst);
}

注意打一个新的agent jar包放在插件模块的libs目录中,接着我们来测试,只拦截m1,不拦截m2:
在这里插入图片描述
在这里插入图片描述
设置一个错误的包前缀,都不拦截:
在这里插入图片描述
在这里插入图片描述

4:在idea中安装

首先打包:
在这里插入图片描述
安装到idea也比较简单,你可以直接下载 然后安装到你的idea中,安装也很简单,可以直接拖拽到idea中松手,会提示你重启,也可以通过file->settings->plugins:
在这里插入图片描述
然后选择zip包安装,也会提示你重启idea,重启后就可以使用该插件了,对了插件的名字叫:debug小助手
在这里插入图片描述

5:实际使用

假设有如下的springboot项目:

@SpringBootApplication
@RestController
public class AAApplication {

    public static void main(String[] args) {
        SpringApplication.run(AAApplication.class);
    }

    @RequestMapping("/sayHi")
    @ResponseBody
    public String sayHi(String name) {
        new MyCls().m1(name);
        return name + ",hi!";
    }
}

测试同学说你的sayHi接口,错误,此时如果你怀疑是入参问题,就可以用这个插件了(是不是就不用debug了🤭🤭🤭,提高效率杠杆的),如下:
在这里插入图片描述

你安装使用了吗?是的话就点个赞让我知道下吧🙁🙁🙁!!!如果你有对这个插件有什么建议,或者其他的想法,就留言告诉我吧,我们一起进步,让自己在这互联网寒潮中不做那批被淘汰的人💪💪💪!!!

写在后面

参考文章列表

Java Agent 介绍和实战 。

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

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

相关文章

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(十五)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 23 - 24 节&#xff09; P23《22.Stage模型-基本概念》 一个应用可以有很多的能力&#xff0c;每个能力可以成为一个 Ability Mod…

百元价位真无线蓝牙耳机怎么选?四款宝藏平价机型盘点

在繁忙的现代生活中&#xff0c;真无线蓝牙耳机凭借其便携性、无线连接以及出色的音质&#xff0c;已经成为了许多人的必备配件&#xff0c;面对市场上琳琅满目的产品&#xff0c;如何在百元价位内挑选出一款性价比高、性能出色的真无线蓝牙耳机&#xff0c;确实是一个值得深思…

CATIA入门操作案例——理工男的浪漫:如何画一支玫瑰花送给女朋友?如何画一束玫瑰花?

目录 引出画玫瑰花画中间的花蕾画花瓣在零件设计模式下&#xff0c;厚轮廓画曲面实体 进行圆周阵列&#xff0c;完整径向对花瓣进行倒角再画一层花瓣画叶子在新的几何体里面绘制叶子 叶子阵列画花杆使用肋条画杆子 将两个零件体进行合并 一支玫瑰花画一束玫瑰花先平移一下复制粘…

异步任务使用场景与实践

异步任务使用场景 根据同步/异步方式划分场景&#xff0c;各场景下常用的技术方案如下&#xff1a; 方式实现特点缺点同步HTTP RPC Cache etc.指标&#xff1a;RT、QPS、TPS、缓存命中率 等&#xff1b; 关注&#xff08;准&#xff09;实时数据&#xff0c;用户可交互1. 处…

微服务架构-线上治理、线下治理与架构演进

目录 一、线上治理 1.1 概述 1.2 线上预案体系 1.2.1 概述 1.2.2 变更引起的故障 1.2.3 流量和容量变化引起的故障 1.2.4 依赖故障 1.2.5 机房、网络等硬件和环境故障 1.2.6 其他 1.2.7 故障的场景化 1.3 基于Metric的预案自动触发 1.4 治理参数动态调整 1.4.1 举例…

dial tcp 10.96.0.1:443: connect: no route to host

1、创建Pod一直不成功&#xff0c;执行kubectl describe pod runtime-java-c8b465b98-47m82 查看报错 Warning FailedCreatePodSandBox 2m17s kubelet Failed to create pod sandbox: rpc error: code Unknown desc failed to setup network for…

算力之困,大模型何解?

互联网企业选择大模型合作伙伴之时&#xff0c;首要考虑的因素是算力。 大模型本身就是巨量参数“力大砖飞”的结晶&#xff0c;也就是说大模型与大算力密不可分。 发展到今天&#xff0c;国内的大模型在对话层面&#xff0c;已经与GPT-3.5接近&#xff0c;但在复杂指令层面与…

使用nvm安装node.js ,方便管理多个版本的node.js,且程序添加和卸载页面也不会出现多个版本的node.js软件(是一个都不会出现)

首先下载和安装nvm windows 安装 nvm&#xff1a; 需要先把本地安装的Node.js卸载&#xff0c;然后再下载nvm&#xff0c;地址&#xff1a; https://github.com/coreybutler/nvm-windows/releases 一般情况&#xff0c;找到最新版本&#xff0c;然后下载nvm-setup.exe文件就可以…

WACV2024论文3D相关速览48篇

WACV2024 3D相关论文阅读 Paper1 Self-Supervised Edge Detection Reconstruction for Topology-Informed 3D Axon Segmentation and Centerline Detection 摘要小结: 许多基于机器学习的轴突追踪方法依赖于带有分割标签的图像数据集。这需要领域专家的手动标注&#xff0c;既…

DP:完全背包+多重背包问题

完全背包和01背包的区别就是&#xff1a;可以多次选 一、完全背包&#xff08;模版&#xff09; 【模板】完全背包_牛客题霸_牛客网 #include <iostream> #include<string.h> using namespace std; const int N1001; int n,V,w[N],v[N],dp[N][N]; //dp[i][j]表示…

从社交网络到元宇宙:Facebook的战略转型

随着科技的迅猛发展和数字化时代的深入&#xff0c;社交网络已不再局限于简单的信息交流和社交互动&#xff0c;而是逐步向更广阔、更深远的虚拟现实空间——元宇宙&#xff08;Metaverse&#xff09;转变。作为全球最大的社交网络平台之一&#xff0c;Facebook正在积极推动这一…

从新手小白到红酒大咖:解锁红酒品鉴的终极秘籍,升级之路全攻略

在五彩斑斓的饮品世界中&#xff0c;红酒以其深邃的色泽、丰富的口感和悠久的历史&#xff0c;吸引了无数人的目光。对于红酒的初学者来说&#xff0c;从小白到品鉴师的道路或许充满了未知与挑战&#xff0c;但只要掌握了正确的知识和方法&#xff0c;就能够轻松踏入这个美妙的…

RAG实操教程langchain+Milvus向量数据库创建你的本地知识库 二

Miluvs 向量数据库 关于 Milvui 可以参考我的前两篇文章 • 一篇文章带你学会向量数据库Milvus&#xff08;一&#xff09;[1]• 一篇文章带你学会向量数据库Milvus&#xff08;二&#xff09;[2] 下面我们安装 pymilvus 库 pip install --upgrade --quiet pymilvus如果你…

SpringBoot整合justauth实现多种方式的第三方登陆

目录 0.准备工作 1.引入依赖 2.yml文件 3. Controller代码 4.效果 参考 0.准备工作 你需要获取三方登陆的client-id和client-secret 以github为例 申请地址&#xff1a;Sign in to GitHub GitHub 1.引入依赖 <?xml version"1.0" encoding"UTF-8&quo…

代码走查的一个实例

1996年6月23日至7月1日&#xff0c;我被点名到四川某单位协助排查某系统的技术问题。 我不懂该系统的原理&#xff0c;也不懂硬件&#xff0c;只能从软件角度分析问题。 那时&#xff0c;我所在单位已经为一家美国公司做了3年的软件第三方独立验证和测试&#xff0c;从中学到…

【51单片机基础教程】点亮led

文章目录 前言51单片机点亮LED的原理硬件部分软件部分51单片机的寄存器编程步骤proteus仿真点亮一个led 点亮多个ledproteus仿真代码 流水灯 总结 前言 单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种集成电路&#xff0c;广泛应用于各种电子产品中。作为嵌入…

1. zookeeper分布式协调者

zookeeper分布协调者 一、zookeeper介绍1、软件设计架构1.1 单体架构1.2 SOA架构/分布式1.3 微服务架构 二、zookeeper角色1、角色2、选举机制3、znode类型 三、zookeeper集群部署1、环境规划2、安装jdk3、安装配置zookeeper3.1 安装zookeeper3.2 编辑配置文件3.3 创建myid文件…

LCP 61. 气温变化趋势

题目 力扣城计划在两地设立「力扣嘉年华」的分会场&#xff0c;气象小组正在分析两地区的气温变化趋势&#xff0c;对于第 i ~ (i1) 天的气温变化趋势&#xff0c;将根据以下规则判断&#xff1a; 若第 i1 天的气温 高于 第 i 天&#xff0c;为 上升 趋势若第 i1 天的气温 等…

WPF 深入理解六、ControlTemplate控件模板

ControlTemplate 定义 控件模板用于来定义控件的外观、样式&#xff0c;还可通过控件模板的触发器(ControlTemplate.Triggers)修改控件的行为、响应动画等。 对与WPF当中,每个控件都是无外观的,这意味着我们可以完全自定义其可视元素的外观,但是不能修改其内部的行为&#xf…

springboot相关的一些知识

SpringBoot可以同时处理多少请求 SpringBoot默认的内嵌容器是Tomcat&#xff0c;所以SpringBoot可以同时处理多少请求取决于Tomcat。 SpringBoot中处理请求数量相关的参数有四个&#xff1a; server.tomcat.thread.min-spare&#xff1a;最少的工作线程数&#xff0c;默认大小…