简介:面向 Java 后端开发者的一款 IntelliJ IDEA 插件,用于将实体类一键转换为 MySQL、Oracle 建表语句以及 JSON 请求体格式,减少手写 SQL 和接口参数样板代码,适合需要频繁建表、撰写接口文档或进行前后端联调的场景。压缩包共 18 个文件,以 4 个 Java 源码、7 个 XML 配置、Markdown 说明与 License 文件为主,包体仅 63KB,轻量小巧,可直接通过 IDE 的本地安装方式加载,也可查看源码学习插件动作注册、菜单扩展等实现细节。插件安装后,在实体类上右键选择 ToMysql、ToOracle 或 ToJson 命令,生成内容即写入系统剪贴板,无需离开编辑器即可完成转换,可显著提升日常开发效率。目前已有 773 人学习下载,项目还附带 README 说明与授权文件,方便二次开发和自定义扩展;对 IntelliJ IDEA 插件开发感兴趣的读者同样值得参考。 手写建表 SQL 这种重复劳动,哪个后端没经历过?字段多了以后,一个字段一个字段对着实体敲 DDL,写完了还要再手工整理一份 JSON 示例给前端联调,碰到 Oracle 和 MySQL 两套库都要支持的项目,工作量直接翻倍。我为什么花时间写这个 IDEA 插件,就是想把“实体类 -> 建表语句 -> 请求体示例”这条链路上的机械化操作全部自动化掉。这个工具的核心能力很简单:选中一个 Java 实体类,右键一按,MySQL 建表语句、Oracle 建表语句、JSON 请求体模板三样东西直接生成好,不用离开 IDE,不用复制类名去网页工具里折腾,更不用手动对齐字段类型和注释。
这个插件适合谁?如果你平时维护的业务系统里有大量的 PO/DO/VO 类,每个类都要对应一张表,接口文档还需要附带 JSON 示例,那你大概率会被这种重复劳动恶心到。这个插件直接面向 Java 后端开发者,尤其是实体类数量多、数据库字段规范严格、接口文档要求结构完整的项目场景。下面我从设计思路、核心实现、实操过程、常见问题几个角度完整拆一遍,代码结构、关键配置和处理逻辑都会放出来,感兴趣的可以直接照着思路自己搭一个,细节足够你落地。
1. 整体设计思路:为什么做成 IDEA 插件,而不是独立工具
先聊设计取舍。当时摆在面前的有两个方向,一个是做成 Maven 插件,在编译期扫描实体类生成 SQL;另一个是做成 IDEA 插件,在开发期通过图形界面直接操作。我最后选了 IDEA 插件,最核心的原因是我希望生成动作和开发动作发生在同一个上下文里。开发者就在写实体类的那个窗口里,手指一按就能看到生成结果,而不是切到命令行跑一个mvn compile,再打开 target 目录翻文件。而且 IDEA 插件可以直接利用 IDE 的语法解析能力,拿到类的完整结构,不需要像 Maven 插件那样通过反射去读取 class 文件,这样即使代码还没编译过,只要是编辑器里能识别的状态就能处理。
插件在技术实现上走了标准的 IntelliJ Platform Plugin 路线。
- 动作入口:继承
AnAction,注册到编辑器右键菜单和Generate菜单组里。 - 模型解析:基于 IDEA 的 PSI(Program Structure Interface)体系读取类名、字段名、字段类型、注释,不需要依赖反射。
- 类型映射:自己维护一套 Java 类型到 MySQL/Oracle 数据类型的映射表,支持注解覆盖默认映射。
- 文本生成:用模板字符串拼接 SQL 和 JSON,不引入额外的模板引擎,减少依赖体积。
- UI 反馈:生成结果统一输出到 IDEA 的 Tool Window,带语法高亮,支持一键复制。
1.1 核心需求解析
把这个插件要解决的三个核心问题拆开看,每个都对应一条独立的生成逻辑。
第一,实体转 MySQL 建表语句。实体类上有类注释、字段注释,字段有类型、长度、是否为空、默认值等信息,这些信息通过 PSI 都能拿到,转成 DDL 的关键在于类型映射的准确性。比如LocalDateTime要映射到datetime,BigDecimal要映射到decimal还要带上精度参数,String默认给varchar(255)但最好允许注解指定长度。还有一个细节是 MySQL 的DROP TABLE IF EXISTS和CREATE TABLE的拼装顺序,以及表名如果和关键字冲突,需要自动加反引号。
第二,实体转 Oracle 建表语句。Oracle 和 MySQL 的 DDL 差异不小,字符串类型要用VARCHAR2,自增主键要用序列加触发器,注释语法完全不同,MySQL 是COMMENT 'xxx'写在列定义的后面,Oracle 是独立的COMMENT ON COLUMN 表名.列名 IS 'xxx'。这些差异必须要在生成器里分别处理,不能只改个数据库类型名就完事。还有分页、驱动这些跟建表无关的东西,插件完全不碰,避免引入无关复杂度。
第三,实体转 JSON 请求体。这个功能的本质是把 Java 对象的结构转成 JSON,但有个场景上的差别,接口文档里的 JSON 示例通常不需要真实值,只需要结构化的示例数据。所以生成策略采用空值 JSON 模板加注释说明,String字段给一个空字符串示例,Integer字段给0,对象嵌套按类结构递归展开,列表字段给一个空数组。这样前端看到的结构和后端实体完全对齐,又不用费劲编造假数据。
1.2 技术选型与依赖范围
IDEA 插件开发的依赖范围是个容易被忽略的坑。开发插件时默认会引入intellij.idea这个 dependency,里面带着完整 IDE 的 jar 包,但如果你只是生成文本、解析 PSI,完全用不到那么重的依赖。用sinceBuild和untilBuild声明好插件兼容的 IDEA 版本。对 Java 版本的要求是 11 以上,因为 IDEA 2020.2 之后的插件运行环境就是 JBR 11 起步。
插件开发本身的工程结构遵循 Gradle 标准,build.gradle 里应用org.jetbrains.intellij插件,设置intellij.version和intellij.type,type 对免费社区版用IC,对旗舰版用IU。本地调试直接runIde任务就能拉起一个沙箱 IDEA 实例,断点调试和普通 Java 开发没有任何区别。我实际开发时踩过一个坑:直接用plugin.xml里声明<depends>com.intellij.modules.java</depends>的话,社区版跑不起来,因为 Java 模块只在旗舰版和社区版里以不同方式存在,正确做法是<depends>com.intellij.modules.platform</depends>加上对 Java PSI 的显式依赖。
2. 核心功能实现:三个生成器的逻辑拆解
插件的核心代码整体上分三大块:实体解析器、SQL 生成器、JSON 生成器。实体解析器负责从 PSI 对象中提取结构化信息,SQL 生成器和 JSON 生成器消费这些结构化信息,分别产出对应文本。
实体解析器是最底层的组件,它的输入是PsiClass,输出是一个中间结构:
public class EntityModel { private String tableName; private String entityComment; private List<FieldModel> fields; } public class FieldModel { private String columnName; private String fieldName; private String fieldType; // 反射类型全限定名,比如 java.lang.String private String columnComment; private Integer length; // 显式长度,没有则为 null private Integer precision; // 精度,用于 decimal private Integer scale; // 小数点位数 private boolean primaryKey; private boolean notNull; private String defaultValue; private boolean autoIncrement; }这个中间结构的好处是数据库方言的差异在生成器这一层隔离开,解析器只用关心 Java 侧的东西。比如主键判断,解析器会检查字段名是否为id,或者是否存在@TableId注解,这些逻辑和具体生成 MySQL 还是 Oracle 的 SQL 无关。后续如果我还想支持 PostgreSQL,只需要新写一个生成器,复用同一套EntityModel就行了。
2.1 实体转 MySQL:类型映射与注释处理
MySQL 生成器拿到EntityModel之后,先拼表名,默认取类名转下划线格式,比如UserOrder会转成user_order。如果类上有@TableName注解,直接用注解值覆盖。这个转换函数是我手写的,核心逻辑就是大小写字母交替处插入下划线,再统一转小写,实测覆盖绝大多数命名场景。
字段类型映射是整个生成过程的核心,我维护了一张映射表:
| Java 类型 | MySQL 类型 | 说明 |
|---|---|---|
| String | varchar(255) | 有 @Column(length=) 时用注解值 |
| Long / long | bigint | 主键且有自增注解时加 AUTO_INCREMENT |
| Integer / int | int | 常规整数 |
| BigDecimal | decimal(10,2) | 精度和标度可从注解读取 |
| LocalDateTime | datetime | 日期时间,也可根据注解转成 date/timestamp |
| LocalDate | date | 仅日期 |
| LocalTime | time | 仅时间 |
| Boolean / boolean | tinyint(1) | 布尔值用 0/1 存储 |
| byte[] | blob | 二进制大对象 |
这里要单独讲讲@Column注解的解析。MyBatis-Plus 的@TableField和 JPA 的@Column都是插件需要支持的注解,解析方式就是读取 PSI 注解属性。注解属性在 PSI 里是PsiNameValuePair,通过PsiAnnotation.findAttributeValue()拿到之后,再去取对应的字符串或数字值。以长度为例,伪代码如下:
PsiAnnotation columnAnno = field.getAnnotation("javax.persistence.Column"); if (columnAnno != null) { PsiNameValuePair lengthAttr = columnAnno.findAttribute("length"); if (lengthAttr != null) { // 解析字符串值,得到 Integer return Integer.parseInt(lengthAttr.getValue().getText().replaceAll("\"", "")); } }还有索引和唯一约束,这些是建表语句里很重要但很容易漏掉的部分。因为 MySQL 的 DDL 里索引通常写在字段定义之后,所以生成器要先收集所有需要建索引的列,最后统一拼到CREATE TABLE的末尾。@Index这种注解在 MyBatis-Plus 里不是标准字段注解,所以我自定义了一个@GenIndex注解,专门给插件消费。
2.2 实体转 Oracle:方言差异处理
Oracle 生成器在整体流程上和 MySQL 生成器是一致的,但细节差异很多,这里展开讲一下最关键的几个点。
第一个是类型映射差异。Oracle 没有varchar,只有varchar2,字符串类型默认最长 4000 字节,和 MySQL 的varchar(255)有本质区别。BigDecimal在 Oracle 里对应number(p,s),如果没给精度就直接number,允许任意精度。LocalDateTime对应timestamp,比 MySQL 的datetime精度更高,默认 6 位小数秒。布尔值在 Oracle 里没有原生的 boolean 列类型,实际项目一般用number(1)存 0/1,或者char(1)存 Y/N,默认生成number(1)。
第二个是主键策略。Oracle 的官方推荐主键生成方式是序列加触发器,BIGINT自增列不能直接写在 DDL 里,要先创建序列:
CREATE SEQUENCE SEQ_USER_ORDER_ID START WITH 1 INCREMENT BY 1 NOCACHE;然后创建触发器:
CREATE OR REPLACE TRIGGER TRG_USER_ORDER_ID BEFORE INSERT ON user_order FOR EACH ROW WHEN (NEW.id IS NULL) BEGIN SELECT SEQ_USER_ORDER_ID.NEXTVAL INTO :NEW.id FROM DUAL; END;这两个对象必须在建表语句之后生成,所以输出结果我设计成三段式:表结构、序列、触发器。拼字符串的时候用三个 StringBuilder 分别维护,最后按顺序输出。用WHEN (NEW.id IS NULL)这个条件,是为了兼容某些场景下手动指定 ID 的情况,不然每次插入都会强制走序列,跟业务上的特殊逻辑冲突。
第三个是注释语法。Oracle 不支持列内联注释,COMMENT ON COLUMN只能独立一行一条。生成的时候我直接在CREATE TABLE结束后统一追加,用--说明这是字段注释语句。这里有个我一直觉得 Oracle 设计得很反人类的地方:注释语句用的列名必须是大写,因为 Oracle 默认存储就是大写列名,如果写小写,注释会挂到一个不存在的列上。所以生成器里要对列名做toUpperCase()。
2.3 实体转 JSON:结构递归与示例值填充
JSON 请求体的生成逻辑相对 SQL 简单一点,但递归结构处理上有个设计决策值得说一下:是直接对PsiClass做结构遍历,还是用反射拿 Java 对象做序列化。我选了前者,因为很多时候实体类还没有编译,甚至在写实体和生成器的当下,代码是编译不过的状态,反射根本走不通。PSI 遍历没有这个限制,只要编辑器的语法分析能通过,它就能拿到类型信息。代价是自己要处理继承和泛型这些反射里现成的能力,但实际实体类转 JSON 这个场景里继承用得并不多,泛型也就是List<Product>这类最基础的用法,手动处理完全可控。
递归遍历的时候用一个JsonBuilder类,它维护一个带有当前缩进级别的可扩展输出结构。每次处理方法调用前先判断字段类型:
- 基本类型或包装类型:直接输出示例值。
String:输出"string",或者根据类名给出更语义化的示例值。- 自定义对象:递归调用,增加缩进。
List<T>:输出[],并在注释里标注元素类型。
嵌套层级过深容易导致 JSON 过长,我设置了一个最大递归层级,默认 5 层,超过就直接输出空对象{}并在注释里提示。实际用下来这个阈值还能接受,因为业务请求体的嵌套深度一般不超过 4 层,5 层已经留了余量。
生成结果支持两种模式:带注释版和纯 JSON 版。带注释版每种字段输出一个注释行,说明“字段名、类型、含义、是否必填”,适合直接贴到接口文档里。纯 JSON 版则去掉所有注释,适合快速放到 Postman 里做联调。这两个模式通过插件的 Settings 配置项切换,不单独做 UI。
3. 实操过程:从安装到生成一份完整建表脚本
下面走一遍实际使用流程。插件开发完打包好的 jar 文件,在 IDEA 里按File -> Settings -> Plugins -> Install Plugin from Disk选择 jar 包安装,重启 IDE 就生效了。需要支持 IntelliJ IDEA 2021.3 及以上版本,实测过 2021.3 到 2024.1 均可正常使用。
安装完成后,打开任意 Java 项目,找到目标实体类。以这个UserInfo为例:
/** * 用户信息表 */ @TableName("t_user_info") public class UserInfo { @TableId(type = IdType.AUTO) private Long id; /** 用户名 */ @Column(length = 64) private String username; /** 密码 */ @Column(length = 128) private String password; /** 邮箱 */ private String email; /** 年龄 */ private Integer age; /** 创建时间 */ @Column(length = 20) private LocalDateTime createTime; }在编辑器里右键,找到Gen SQL & JSON菜单项,展开后有三个子项:MySQL DDL、Oracle DDL、JSON Request。任意点击一个,IDEA 底部会弹出插件的输出面板,里面就是生成好的文本。
这是 MySQL DDL 的输出结果:
-- 表: t_user_info / 注释: 用户信息表 DROP TABLE IF EXISTS `t_user_info`; CREATE TABLE `t_user_info` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `username` varchar(64) DEFAULT '' COMMENT '用户名', `password` varchar(128) DEFAULT '' COMMENT '密码', `email` varchar(255) DEFAULT '' COMMENT '邮箱', `age` int DEFAULT NULL COMMENT '年龄', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';注意几个细节:id因为带了@TableId(type = IdType.AUTO)注解,插件自动识别为自增主键;username因为带了@Column(length = 64),生成的长度是 64,不是默认的 255;create_time因为是LocalDateTime类型,自动映射成了datetime。这些细节都是类型映射配置生效的直接体现。
Oracle DDL 的输出则完全不同:
-- 表: t_user_info / 注释: 用户信息表 DROP TABLE t_user_info; CREATE TABLE t_user_info ( id NUMBER(19) NOT NULL, username VARCHAR2(64) DEFAULT '', password VARCHAR2(128) DEFAULT '', email VARCHAR2(255) DEFAULT '', age NUMBER(10), create_time TIMESTAMP DEFAULT SYSTIMESTAMP, CONSTRAINT PK_T_USER_INFO PRIMARY KEY (id) ); -- 序列 CREATE SEQUENCE SEQ_T_USER_INFO_ID START WITH 1 INCREMENT BY 1 NOCACHE; -- 触发器 CREATE OR REPLACE TRIGGER TRG_T_USER_INFO_ID BEFORE INSERT ON t_user_info FOR EACH ROW WHEN (NEW.id IS NULL) BEGIN SELECT SEQ_T_USER_INFO_ID.NEXTVAL INTO :NEW.id FROM DUAL; END; -- 字段注释 COMMENT ON COLUMN t_user_info.id IS '主键ID'; COMMENT ON COLUMN t_user_info.username IS '用户名';表名没有加引号是因为在 Oracle 里不加引号自然就是大写存储,符合规范。NUMBER(19)是分配Long类型自然映射到的精度,不会像 MySQL 的bigint那样看一眼就知道有 8 字节,但语义上是一致的。注释全部放在建表语句之后,这是为了保持脚本在 SQL*Plus 里能顺序执行,不会因为注释打断语句块。
3.1 输出面板与一键复制
输出面板的设计参考了 IDEA 自带的数据库工具:顶部是一个JTextArea做只读展示,底部放两个按钮,“复制全部”和“关闭”。复制按钮注册了CopyPasteManager,点击后直接写入系统剪贴板,方便转到 Navicat 或 PL/SQL Developer 里执行。面板内容按生成类型自动做语法高亮,SQL 用浅蓝色区分关键字,JSON 用不同颜色区分键名和字符串值,这个效果是通过 JEditorPane 的HTMLDocument做的,没有引入完整语法高亮框架。
颜色高亮这个功能原本没打算做,但实际用了几天后发现没有高亮找字段名很费劲,尤其是字段多的时候。我花了大约半天时间写了一个简单的文本高亮器,维护一个 SQL 关键字集合,遍历输出文本,对关键字和注释分别上色。在数据量上来之后,还是比看纯文本舒服很多。
提示:如果 SQL 里有中文注释,需要确认 IDEA 沙箱环境的默认字符集是 UTF-8,否则贴到 Windows 的 CMD 窗口执行时可能乱码。这个问题不影响生成,但会影响显示,注意运行
-Dfile.encoding=UTF-8启动参数即可。
3.2 配置项与自定义扩展
插件在 IDEA 的Settings -> Tools -> Gen SQL & JSON下面提供了几个配置项,默认值就能满足大部分场景,但做特殊定制时很有用:
- 默认字符串长度:默认 255,可改成 128 或 64。
- 表名前缀:比如统一加
t_,默认关闭(按类名原样转换)。 - 默认 MySQL 引擎:默认
InnoDB,可选MyISAM。 - 默认字符集:默认
utf8mb4,可选utf8。 - JSON 生成模式:默认带注释,可选纯 JSON。
- Oracle 主键方式:默认序列加触发器,可选无主键策略(只建表)。
这些配置会存到PersistentStateComponent里,IDEA 自动管理序列化和持久化,不用自己写配置文件读写。没有提供过多开关,是刻意控制复杂度,因为配置项的边际收益递减,加太多反而让设置界面难用。
4. 常见问题与排查技巧
开发和使用这个插件过程中,我先后遇到过不少问题,这里挑几个典型的整理成速查表,后续维护或者二次开发时可以少踩点坑。
| 问题 | 表现 | 排查与解决 |
|---|---|---|
| 插件安装后菜单不显示 | 右键菜单里找不到 Gen SQL & JSON | 检查 plugin.xml 里 action 注册的parentGroup,可能需要改成EditorPopupMenu而不是GenerateGroup |
| 字段注释总是为空 | 生成 SQL 里 COMMENT 是空字符串 | PSI 取注释时要用PsiDocComment,直接从field.getFirstChild()找不一定能拿到;推荐用field.getDocComment() |
| 类型映射不生效 | LocalDateTime被映射成varchar | 检查类型匹配用的是全限定名还是简单类名,LocalDateTime的 PSI 类型拿到的是java.time.LocalDateTime,匹配时要全名比较 |
| 多模块项目扫不到实体 | 右键时提示无法解析类 | ActionEvent的PsiFile可能为空,需要通过CommonDataKeys.PSI_FILE再取一次,同时判断event.getProject() |
| 重复生成带括号的默认值 | 默认值CURRENT_TIMESTAMP被加了两层括号 | 生成 SQL 时要注意,MySQL 里DEFAULT CURRENT_TIMESTAMP不需要括号,但DEFAULT ('abc')需要;建议默认值统一走一个 escape 函数,判断内部是否已带括号 |
| IDEA 升级后插件失效 | 报PluginIncompatibleException | 修改 build.gradle 里的untilBuild,或者用DynamicPluginV2声明兼容新版本 |
4.1 踩过的坑:PSI 注释获取的正确姿势
PSI 注释获取是插件开发中最基础也最容易出错的一块。JDK 文档注释在 PSI 里的呈现是PsiDocComment,它挂在PsiField上,但获取方式不能想当然地直接遍历子节点。我在最初版本里遍历field.getChildren()找PsiDocComment,结果经常找到的是空节点。后来改用官方推荐的PsiDocCommentUtil.getDocComment(field),稳定可靠。
不同注释格式的解析在生成时也要区分。/** 用户名 */取文本内容时会带上*和空格,需要做一个清洗,把每行首尾的空白字符去掉,再把行首的*去掉,最后拼接成一个单行字符串。这是个小而常见的坑,很多文档注释是多行的:
/** * 用户名 * 同时用于登录和展示 */如果直接取原始文本拼接,生成出来的 SQL 注释会变成用户名\n * 同时用于登录和展示,在 SQL 里会断行出错。清洗函数必须把这些换行和星号处理干净。
4.2 踩过的坑:IDEA Action 注册时的isEnabled条件
Action 注册到右键菜单后,不在 Java 文件上点击时菜单项应该置灰,不然用户在任何文件上(比如 XML、properties)都会看到 Gen SQL & JSON 菜单,点了之后弹一个莫名其妙的错误。这个需求用AnAction的update()方法实现,在里面判断当前文件类型:
@Override public void update(AnActionEvent e) { PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE); boolean enabled = psiFile instanceof PsiJavaFile; e.getPresentation().setEnabledAndVisible(enabled); }setEnabledAndVisible这个方法同时控制了可用状态和可见状态,比较省心。实测还有一个小小的联动问题:如果当前选中的是类名而不是整个类文件,PsiFile还是能正确返回PsiJavaFile,但PsiElement可能指向类名这个节点,需要往上找到PsiClass再操作。正确做法是先用CommonDataKeys.PSI_ELEMENT拿当前元素,然后递归往上找PsiClass父节点。
4.3 踩过的坑:递归解析时碰到循环引用
实体类 A 里有一个字段是 B 类型,B 里又有一个字段是 A 类型,这种双向关联在业务里很常见。做 JSON 生成时如果不加处理,递归会无限嵌套下去,直到栈溢出。我的方案是为每个已访问的类维护一个“解析中”集合,进入解析前先检查该类是否已在集合中,在就返回{}并附加注释“循环引用,已截断”。这样既防止了栈溢出,又保留了一定可读性。
处理 SQL 生成时,循环引用不存在问题,因为建表只分析当前一个表的字段,不会去生成关联表的 DDL。但如果我后续想扩展“生成所有关联表”的功能,就必须先做拓扑排序确定建表顺序,否则外键引用会失败。这是一个明确标记的待扩展点。
5. 后续扩展思路与建议
插件目前的版本已经能覆盖日常开发中的常见场景,但还有一些方向可以继续完善,按优先级从高到低排列:
- 支持 PostgreSQL 方言。PostgreSQL 的 DDL 和 MySQL 又有很大差别,比如
SERIAL自增、TEXT类型、COMMENT ON语法(和 Oracle 很像),类型映射表增加一个方言枚举就能接入。 - 支持导出增量 SQL(只输出变更字段的 ALTER TABLE 语句)。这个功能的难点在于需要先解析现网表的实际结构,不能只靠实体类,实体类和现网表的字段差异比对可以采用字符串拉链算法实现。
- 支持从 SQL 反向生成实体类。不只是从 Java 到 SQL,还要能从 SQL 到 Java,这在接手老项目、根据数据库表反推实体时非常有用。
- 增加模板引擎(如 Freemarker 或 Velocity),让用户自定义生成模板。这个对个性化需求多的团队帮助很大,但对插件本身的结构要求会更高,文本生成从硬编码字符串切换到模板渲染,意味着单元测试的基准也要改。
选择优先做 PostgreSQL 的原因很现实:身边的项目用 MySQL 和 Oracle 偏多,但 PostgreSQL 的用户基数也在快速增长,一旦有人提出“能不能也支持 PG”,这时候再回来改类型映射、改自增策略、改注释语法,改动范围不小。提前把方言抽象做好,后续接 PG 的时间可以压缩到一周以内。
6. 总结与个人体会
我在实际开发这个插件的过程中,最大的体会是:写 IDEA 插件跟写普通 Web 后端的思路差别很大,核心不在于你能调多少 API,而在于你对“IDE 内部的数据结构”理解得清不清楚。PsiClass不是反射里的Class,它存在的前提是语法分析成功,默认严格模式,宁可拿不到信息也不降级。类型映射表的设计决定了这个插件 80% 的价值,务必一开始就设计得可配置,不要写死在 switch-case 里。
最后再分享一个非常实用的调试技巧:IDEA 插件开发时,直接在build.gradle里配置runIde的 JVM 参数,比如jvmArgs '-Xmx2G',能避免沙箱 IDEA 因内存不足崩溃。调试快捷键和日常开发保持一致,断点一旦命中就能看到 PSI 树的全貌,这一步对理解 PSI 结构帮助极大,也让我从“写面向对象的 Java”切换到了“写面向 PSI 结构的 Java”,这个坎一迈过去,插件开发的大部分难点就已经解决了。
本文还有配套的精品资源,点击获取