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

2024-06-22 06:30

本文主要是介绍idea插件开发之一起来开发个打印方法入参和返回值的插件吧!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在前面

源码 。
在开发过程中为了调试代码我们就可能就需要知道某个方法入参的值是什么,或者是返回值是什么。此时,我们的解决办法一般都是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 {@RuntimeTypepublic 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 {@Overridepublic 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.jarvmParametersList.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 {@Overridepublic 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,myFn2System.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")@ResponseBodypublic String sayHi(String name) {new MyCls().m1(name);return name + ",hi!";}
}

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

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

写在后面

参考文章列表

Java Agent 介绍和实战 。

java agent技术原理及简单实现 。

这篇关于idea插件开发之一起来开发个打印方法入参和返回值的插件吧!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1083536

相关文章

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

判断PyTorch是GPU版还是CPU版的方法小结

《判断PyTorch是GPU版还是CPU版的方法小结》PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIACUDA)上运行,所以对于深度学习开发者来说,正确识别PyTor... 目录前言为什么需要区分GPU和CPU版本?性能差异硬件要求如何检查PyTorch版本?方法1:使用命

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Java中的工具类命名方法

《Java中的工具类命名方法》:本文主要介绍Java中的工具类究竟如何命名,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java中的工具类究竟如何命名?先来几个例子几种命名方式的比较到底如何命名 ?总结Java中的工具类究竟如何命名?先来几个例子JD

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

《IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决》:本文主要介绍IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决,本文分步骤结合实例给大... 目录步骤 1:创建 Maven Web 项目步骤 2:添加 Spring MVC 依赖1、保存后执行2、将新的依赖

python获取网页表格的多种方法汇总

《python获取网页表格的多种方法汇总》我们在网页上看到很多的表格,如果要获取里面的数据或者转化成其他格式,就需要将表格获取下来并进行整理,在Python中,获取网页表格的方法有多种,下面就跟随小编... 目录1. 使用Pandas的read_html2. 使用BeautifulSoup和pandas3.

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊