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

相关文章

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S

MyBatis-Plus 中 nested() 与 and() 方法详解(最佳实践场景)

《MyBatis-Plus中nested()与and()方法详解(最佳实践场景)》在MyBatis-Plus的条件构造器中,nested()和and()都是用于构建复杂查询条件的关键方法,但... 目录MyBATis-Plus 中nested()与and()方法详解一、核心区别对比二、方法详解1.and()

golang中reflect包的常用方法

《golang中reflect包的常用方法》Go反射reflect包提供类型和值方法,用于获取类型信息、访问字段、调用方法等,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录reflect包方法总结类型 (Type) 方法值 (Value) 方法reflect包方法总结

IDEA中新建/切换Git分支的实现步骤

《IDEA中新建/切换Git分支的实现步骤》本文主要介绍了IDEA中新建/切换Git分支的实现步骤,通过菜单创建新分支并选择是否切换,创建后在Git详情或右键Checkout中切换分支,感兴趣的可以了... 前提:项目已被Git托管1、点击上方栏Git->NewBrancjsh...2、输入新的分支的

C# 比较两个list 之间元素差异的常用方法

《C#比较两个list之间元素差异的常用方法》:本文主要介绍C#比较两个list之间元素差异,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 使用Except方法2. 使用Except的逆操作3. 使用LINQ的Join,GroupJoin

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四