IntelliJ插件开发:如何收集addMapper注册的Mapper字符串键
IntelliJ插件收集已注册Mapper字符串实现方案
第一步:建立Command实现类与注册宿主的绑定关系
你需要先扫描项目中所有对CommandManager.registerCommand方法的调用,基于IDEA PSI的方法调用遍历即可实现:
- 匹配方法时不要仅靠方法名判断,要校验方法的全限定名,避免匹配到用户自定义的同名方法
- 对每个合法的调用点,解析第一个参数:如果是
XxxCommand.class形式的类字面量,直接拿到对应的PsiClass就是目标Command实现类 - 解析第二个参数的类型,拿到对应的宿主类(也就是示例中传入
this对应的、实现了specialMethod的类) - 维护映射关系:
Command实现类PsiClass -> 对应宿主PsiClass,如果子类未重写specialMethod,要向上遍历父类查找注册逻辑。
第二步:从宿主类中提取所有已注册的Mapper键
对每个绑定好的宿主类,定位它重写的specialMethod方法,遍历方法体内所有调用:
- 匹配所有方法名为
addMapper、调用者类型为CustomCommand的方法调用,同样要校验方法全限定名避免误匹配 - 对每个匹配的
addMapper调用,取第一个参数:- 如果是字符串字面量(
PsiLiteralExpression类型),直接取字符串值作为已注册的mapper键 - 如果是常量引用(比如传入类静态常量),递归解析常量的初始值拿到实际字符串
- 如果是动态拼接、方法返回值这类无法静态确定值的参数,直接跳过,不要强行解析避免误判
- 如果是字符串字面量(
- 注意要处理链式调用场景:如果是
CommandManager.registerCommand(xxx, this).addMapper("a", ...)这种写法,要顺着方法调用链继续向后匹配addMapper调用,不要漏收集。
第三步:处理作用域与结果去重
- 扫描范围用
GlobalSearchScope做过滤:只收集当前项目源码、你自有API依赖范围内的调用,不要把其他第三方依赖、测试代码里的注册逻辑算进来 - 如果同一个Command类在多个宿主类中被注册,要把所有宿主下收集到的mapper字符串合并去重,作为该Command类对应的合法mapper集合
- 排除已经在当前Command类中被
@Mapper注解占用的字符串,后续补全的时候只返回未被使用的键即可。
第四步:做结果缓存避免性能问题
不要每次触发代码检查、补全都重新全量扫描所有调用点,用IDEA的CachedValue做缓存:
- 缓存key绑定
PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT,只要Java源码有改动就自动清空缓存 - 缓存结构直接存
Map<PsiClass, Set<String>>,key是Command实现类,value是对应类所有合法的已注册mapper字符串集合。
踩坑提示:如果遇到command对象被传递到其他方法里再调用
addMapper的场景,静态分析无法100%覆盖数据流,直接跳过这类场景即可,不要做过重的数据流分析,否则既影响性能又容易出现误判。
拿到这个缓存的映射之后,写检查逻辑的时候,只要拿到@Mapper注解所在的Command类,就能直接取出所有合法的已注册mapper集合,判断注解里的字符串是否存在;写补全逻辑的时候,直接把集合里未被当前类@Mapper使用的字符串作为补全项返回即可。
内容的提问来源于stack exchange,提问作者Andret2344
相关产品推荐
相关产品推荐

