插件开发的那些事儿:一个五年码工的实战总结
引子:插件开发,不止是“写个扩展”那么简单

做开发这些年,我一直觉得 IDE(集成开发环境)是程序员最亲密的伙伴。从写代码、调试到版本控制,IDE 扮演的角色远比编辑器更复杂。而在实际项目中,我们常常遇到一些需求,无法通过现有的工具链满足——比如自动化的日志清理、自定义的代码检查逻辑,或者某些特定业务场景下的辅助功能。
这时候,IDE 插件开发就成了一个非常实用的解决方案。我第一次真正深入地接触插件开发,是在一次重构项目中需要自动识别并标注出“可能被废弃”的类和方法。那是一个典型的“小需求”,但如果不解决,每天都会有人踩坑。于是,我决定尝试用 IntelliJ IDEA 插件的形式来实现这个想法。
这篇文章,我就结合那次实战经历,谈谈我对 IDE 插件开发的理解、挑战和收获。
问题描述:我们需要一种“静态提示机制”

事情起源于我们在进行老系统重构时的一个痛点:
有些类和方法已经过期,但由于文档缺失或历史包袱,仍然会被误用。
最初我们采用的是人工提醒+代码 Review 的方式,但随着团队人数增加、新成员不断加入,这种方式逐渐失效。于是我们希望能够在 IDE 里面,以类似于 Linter 或 Inspection 的形式,在编码阶段就给出提示:“这个类/方法即将弃用,请使用新的方式。”
理想情况下,这种提示应该具备以下能力:
- 能够识别代码中的注解(如
@Deprecated("Use NewClass")) - 在代码中高亮显示
- 提供 Quick Fix 跳转到新类
- 不影响现有编译流程
当时市面上有一些现成的插件,但都无法满足我们的定制化需求。我们最终决定自己动手,开发一款适用于内部项目的 IDEA 插件。
解决方案:从零开始构建我们的 Inspection 插件

我们的目标很明确:在代码编辑器中实时标记“被标记为废弃”的类和方法,并提供跳转建议。
技术选型与架构设计
首先得确定平台:我们公司主要用 IntelliJ IDEA,所以自然选择 JetBrains 平台的插件开发框架——基于 IntelliJ Platform SDK。
技术栈如下:
- Java
- Kotlin(部分模块)
- 使用 JetBrains 提供的 PSI(程序结构接口)API 进行语法分析
- 利用了
com.intellij.codeInspection包下的 Inspection 工具链
整体架构大致如下:
[PSI Tree] --> [Custom Annotator / Inspection] --> [Highlight & Show Message] --> [QuickFix]
简单来说:
- 插件监听当前打开的 Java 文件;
- 通过 PSI 遍历所有类、方法;
- 检查是否有自定义注解(我们用了一个叫
@Discarded的自定义注解); - 如果有,则在编辑器中高亮显示;
- 同时支持点击后弹出 QuickFix,跳转至指定新类或方法。
实现思路拆解
1. 定义规则入口点:创建 Inspection 类
public class DeprecatedUsageInspection extends AbstractBaseJavaLocalInspectionTool {
@NotNull
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitMethod(PsiMethod method) {
if (hasDeprecatedAnnotation(method)) {
holder.registerProblem(
method.getNameIdentifier(),
"该方法已弃用,请参考QuickFix",
ProblemHighlightType.LIKE_DEPRECATED,
new ReplaceWithNewMethodQuickFix()
);
}
}
// 同理处理类名等其他元素
};
}
private boolean hasDeprecatedAnnotation(PsiMethod method) {
// 实现判断是否含有 @Discarded 注解
}
}
这段代码的核心是通过 ProblemsHolder 来注册一个问题节点,并绑定一个 QuickFix 类。
2. 实现 QuickFix:让提示可操作
public class ReplaceWithNewMethodQuickFix implements LocalQuickFix {
@Override
public @NotNull String getName() {
return "查看替代方法";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiIdentifier) {
PsiMethod method = (PsiMethod) element.getParent();
// 获取到新类信息,执行跳转
NavigateToReplacementUtil.navigateToReplacement(project, method);
}
}
}
这部分的关键在于如何解析原始方法上的注解内容,并定位到替代类或方法。
3. 支持自定义注解
为了适配不同的项目规范,我们允许开发者在注解中指定替代方法:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Discarded {
String replacement() default "";
}
这样,当用户使用该注解时可以写:
@Discarded(replacement = "com.example.NewService.doSomething")
public void oldMethod() { ... }
插件在发现此类注解后,会提取 replacement 字段用于跳转。
踩坑经验:别怕踩坑,坑是成长的阶梯
整个过程中,我们踩了不少坑,也学到了很多教训。以下是几个印象深刻的地方:
坑一:PSI 的异步加载问题
一开始我们直接在 buildVisitor() 中调用了一些比较耗时的操作(比如类路径扫描),结果导致编辑器卡顿严重。后来才知道,IDEA 的 Inspection 是运行在 UI 线程上的,必须避免任何耗时操作。
✅ 解决办法:
将复杂的逻辑(如依赖解析)放到后台线程中处理,并通过 ProgressIndicator 控制进度条。
ApplicationManager.getApplication().executeOnPooledThread(() -> {
// 执行耗时任务
});
坑二:不同项目结构带来的兼容性问题
不同项目使用的类加载机制不一致,有的用 Maven,有的用 Bazel,还有的是多模块嵌套结构。一开始我们在获取类引用的时候频繁出现 NPE。
✅ 解决办法:
借助 JavaPsiFacade 来获取类文件,而不是直接依赖字符串匹配。
JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
坑三:插件测试难上线快的问题
插件上线前要做充分测试,但我们发现每次手动安装插件再重启 IDEA 非常低效,尤其是在迭代初期频繁修改的情况下。
✅ 解决办法: 使用 IDEA 社区版 + Run Configuration 的方式快速调试。在 plugin.xml 里加上本地 dev 模式即可直接运行调试。
效果总结:从“被动提醒”到“主动防御”
这套插件上线后,极大地减少了因为使用旧 API 导致的错误和返工。我们还在公司内部推广开来,现在已经成为 Code Review 流程的一部分。
具体效果包括:
- 新人上手成本降低,因为代码中会有“提示灯”
- 开发人员在敲代码时就能看到建议,无需等到 Review 阶段
- 对废弃类的追踪更加统一,避免了“散装迁移”
- 未来计划对接 A/B Test 框架,动态开关某些提示级别
可以说,这次插件开发让我们实现了从“代码监控”到“智能引导”的转变。
经验分享:插件开发到底值不值得?几点建议
如果你也在考虑要不要做一个 IDE 插件,以下是我这几年积累下来的一些思考:
✅ 插件开发适合这些情况:
- 你有持续性的、重复性的代码问题需要解决
- 你所在的团队缺乏某种“轻量级自动化”的机制
- 你想打造一套属于自己的开发工具链
❌ 不太推荐的情况:
- 临时性的需求(比如只用一次的脚本)
- 现有工具已经足够解决你的问题(比如 ESLint、Checkstyle 等)
💡 我的经验建议:
从小做起,先验证可行性。
插件开发不是小事,一开始就想着大而全容易迷失方向。最好先做个最小可运行的原型,确认能解决问题,再继续优化。多关注官方文档和社区资源。
JetBrains、Eclipse 和 VSCode 都有成熟的插件生态,不要闭门造车。官方的 SDK 文档、Sample Code、论坛都非常重要。重视测试和用户体验。
插件是给别人用的,不能影响性能或稳定性。多用 Unit Test + Integration Test 结合调试模式,确保每个环节不出问题。保持更新节奏,定期维护很重要。
IDE 更新频繁,插件也需要跟进。最好设专人维护或建立更新机制,避免半年后变成“死插件”。
写在最后:插件不只是工具,更是工程文化的沉淀
说实话,一开始我以为这只是个小项目,花几天时间搞完就行了。但在实际开发和后续维护的过程中,我才意识到它其实承载了很多东西:
- 它是我们对代码质量的一种态度
- 它是对新人友好的体现
- 它帮助我们建立起一套持续改进的文化
有时候我们会笑称:“一个好插件顶得上三个 Reviewer。” 但这背后,其实是无数个细节和打磨。
IDE 插件开发从来不只是“加个按钮”或“高亮一下”那么简单,它是你在工程世界中留下的印记,是你对效率和质量的坚持。
如果你刚好也准备踏上这条路,不妨试试看。也许下一个改变别人编码习惯的,就是你写的那个小小插件。
最后附一句个人感悟:写插件就像修桥铺路,虽然不起眼,但每一条正确的指引,都是通往更好代码世界的捷径。

评论 0