# LazycatIdeaPlugin **Repository Path**: leavingme/lazycat-idea-plugin ## Basic Information - **Project Name**: LazycatIdeaPlugin - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-08 - **Last Updated**: 2026-11-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Lazycat IDEA Plugin 为 [Lazycat](https://gitee.com/lwm/lazycat) 框架提供 IntelliJ IDEA 开发辅助的插件。 Lazycat 是一个具备 Spring 核心能力的轻量级 Java Web 框架(IOC、AOP、事务、JDBC、HTTP 路由、静态资源), 目前没有 IDE 支持。本插件通过 PSI 分析源码,提供路由、Bean、配置项相关的高亮、跳转、补全和检查。 > 当前阶段重点:按 Lazycat 的真实容器加载流程标注**所有容器 Bean**(类图标、`@Bean` 方法图标、 > Beans 工具窗口),并保证 `@Inject` 的未使用/未解析提示准确。HTTP 路由能力保留自早期版本, > 本阶段未继续扩展。 ## 功能 ### 路由 / 接口 - **Endpoints 工具窗口**:列出项目中所有由 Lazycat 注册的端点,支持按 HTTP 方法、路径、控制器、方法名过滤。 - 双击端点跳转到对应方法;按钮可复制完整 URL;支持手动刷新。 - 控制器方法左侧显示路由图标,悬停查看 `GET /path`,点击复制完整 URL。 - `Alt+Enter` → `复制 Lazycat URL`。 - 路由规则与 `ControllerResolver` 保持一致: - 仅 `@Controller` Bean 中 **public、非 static、非 abstract** 的方法; - `@Get/@Post/@Put/@Patch/@Delete` 以及通过 `@Http` 组合的自定义注解(含 `HEAD` 等 handlerType); - 路径由控制器路径与方法路径拼接(`PathUtil` 规则); - 支持从父类继承的路由方法。 ### IOC / Bean - **Lazycat 工具窗口的 Beans 页签**(`View → Tool Windows → Lazycat`):列出全局扫描得到的容器 Bean, 可按名称、类型、来源、配置类过滤;双击跳转到声明,支持复制名称和手动刷新。 工具窗口中 `Beans` 页签默认排在 `Endpoints` 左侧。 - 容器中的类 / `@Bean` 方法左侧显示 Bean 图标(右下角带下拉三角,表示点击后弹出导航菜单), 悬停查看 Bean 名称、类型和注册来源。 由 `@Bean` 方法创建并注册到容器的类型同样显示图标:即使类上没有任何 Bean 注解, 只要其实例来自容器,也会标记为容器成员,避免被误认为普通类。 点击图标弹出与 Spring 插件类似的导航菜单: - 跳转到 `@Inject` 注入点:列出注入该 Bean 的所有字段/参数,多个时弹出选择框; - 跳转到 Bean 声明; - 在 Lazycat Beans 工具窗口中定位; - 复制 Bean 名称。 图标以**实际容器成员**为准,而不是只看类上有没有 `@Service` / `@Bean`。 - `@Inject` 字段左侧显示 Bean 图标(右下角带向右箭头,表示点击直接跳转), 点击跳转到对应 Bean 声明;有多个候选时弹出选择框。 - `@Inject` / `@Value` 字段通过 `ImplicitUsageProvider` **只标记为隐式写入**(容器反射注入 / 配置赋值), 不标记为隐式读取或隐式使用:这样既不会误报“字段未赋值”,又能让 IDEA 按业务代码是否读取字段 来显示颜色——未被引用的私有注入/配置字段保持灰色,已被读取的字段显示正常颜色。 - `META-INF/lazycat/beans` 文件图标右下角带绿色齿轮;`application.yml` / `application-*.yml` / `application.properties` 文件图标右下角带灰色六边形标记。 - 容器中的类、`@Bean` 方法与容器 `@Controller` 中的 HTTP 方法也不会被标记为“未使用”: 框架通过反射创建/调用它们,插件根据容器索引把它们标记为隐式使用。HTTP 方法以方法上的 `@Http` 为准(`@Get` / `@Post` / `@Put` / `@Patch` / `@Delete` 及自定义注解通过元注解 `@Http` 识别),类不在容器中时不受影响。 - `Ctrl+Click`(或 `Cmd+Click`)`@Inject` 字段/参数跳转到 Bean 声明: - 指定名称时按名称查找; - 未指定名称时按类型查找,支持 `List`、`T[]` 集合注入。 - `@Inject` 字符串内补全项目中的 Bean 名称。 - 检查无法解析的 `@Inject`,以**错误**(红色波浪线)标注在 `@Inject` 注解上。 - 容器成员按 `BeanContextStarter` 的实际加载顺序从源码与类路径重建: - 启动类(`main` 中调用 `Application.run()` / `Application.start()`,或构造 `Application`)本身注册为 Bean, 并按配置类处理;仅有 `main` 的启动器 / CLI 不会被误认为 Lazycat 启动类。 依赖尚未解析时,若启动类带 `@Configuration` / `@Import` / `@BeanScan` / `@MapperScan`,也可识别; - 启动类扫描其所在包及子包;非空 `@BeanScan` 的每个值追加 `.**` 后扫描, 仅注册带 `@Bean`(含元注解、父类/接口继承)的类; - 只索引生产源码,`src/test` 中的类不参与容器重建; - `@Configuration` 类的 public 非 static `@Bean` 方法(含继承,镜像 `ReflectUtil.findDeclaredMethods` 的按方法名覆盖规则); - 递归 `@Import`(`BeanDesignImportRegistrar` 本身不注册为 Bean); - 类路径(项目资源、模块输出目录、依赖 jar)中所有 `META-INF/lazycat/beans`; - MyBatis-Flex `@MapperScan` 动态注册的 `BaseMapper` 接口。 - Bean 名称遵循 `BeanDesignBuilder#initBeanName`:`@Bean.value` 优先;`@Service` / `@Configuration` 通过 `@AliasTo(annotation = Bean.class)` 提供名称;`@Controller.value` 是请求路径,不作为 Bean 名称; 未指定时使用方法名或首字母小写的类名。 - 多个启动类按容器并集处理;没有启动类的模块退化为全项目 Bean 扫描。 ### 配置项 - `Ctrl+Click` `@Value("${key}")` 中的字符串跳转到声明该 key 的配置位置: 同一个 key 在多个配置文件(基础配置、多个 profile,含未激活 profile)中声明时, 弹出与 IDE 一致的“选择声明”下拉框,每项显示配置值、文件名和模块, 选中后直接定位到对应配置行。 - `Alt+Enter` → `打开 Lazycat 配置项 "key"` 精确定位到配置行。 - `@Value` 占位符内补全配置 key(支持 `${key:default}` 语法)。 - `@SettingPrefix` 补全已存在的配置前缀。 - `@PathParam` 补全当前路由的路径变量。 - 检查缺失的 `@Value` 配置 key、缺失的默认值等。 ### 代码检查 | Short Name | 说明 | | --- | --- | | `LazycatRoute` | 路由方法不在 `@Controller` 中、非 public/static/abstract、一个方法多个 HTTP 注解 | | `LazycatParameterBinding` | 路由方法参数缺少/重复绑定注解、`@PathParam` 名称不存在于路径、`@Header` 不被运行时处理 | | `LazycatInject` | 找不到名称或类型的 Bean | | `LazycatValue` | `@Value` 占位符格式错误、配置文件中找不到 key | | `LazycatTransactional` | 类级 `@Transactional` 不生效、非 public/static/final 方法无法被代理 | ### 实时模板 `Settings → Editor → Live Templates → Lazycat`: - `lccontroller`、`lcservice`、`lcget`、`lcpost`、`lcbean`、`lcinject`、`lcvalue` ## 环境要求 - JDK 25(编译 IntelliJ Platform 2026.2 所需;插件字节码仍输出为 Java 21,以兼容 2025.1+) - Gradle 9.9 - IntelliJ IDEA 2025.1 – 2026.2(`sinceBuild=251`,`untilBuild=262.*`) ## 构建与安装 ### Windows 一键脚本 `scripts\install-gradle-and-plugin.cmd` 会自动完成: 1. 下载 Gradle 9.9 到 `D:\HUAWEI\Software\ENV\gradle-9.9.0`,并用官方 SHA-256 校验; 2. 使用 `D:\HUAWEI\Software\ENV\jdk-21` 运行 Gradle,编译工具链自动使用 JDK 25,执行 `clean test buildPlugin`; 3. 将构建产物安装到检测到的 IDEA 2025.1–2026.2 用户插件目录(旧版本会备份到临时目录)。 ```bat scripts\install-gradle-and-plugin.cmd ``` 可选参数:`-SkipBuild`(使用已有产物,不重新构建)、`-SkipInstall`(只构建)、`-RunIde`(构建后启动沙箱 IDEA)。 只安装 Gradle 时使用 `-SkipBuild -SkipInstall`。 如自动安装未找到 IDEA 配置目录,脚本会打印 zip 路径,可手动通过 `Install Plugin from Disk...` 安装。 ### 手动构建 项目已包含 Gradle Wrapper(Gradle 9.9.0),直接执行: ```bash ./gradlew buildPlugin ``` Windows 下为 `gradlew.bat buildPlugin`。 构建产物位于 `build/distributions/lazycat-idea-plugin-0.2.0.zip`。 在 IDEA 中打开 `Settings → Plugins → ⚙ → Install Plugin from Disk...` 选择该 zip 安装并重启。 开发调试: ```bash ./gradlew runIde # 启动带插件的沙箱 IDEA(需下载对应 IDEA 发行版) ./gradlew test # 运行纯逻辑单元测试 ``` 也可以直接用 IDEA 打开本目录,使用 Gradle JDK 21+ 同步后运行 Gradle 任务(编译工具链需要 JDK 25,可放在 `~/.jdks` 下供 Gradle 自动检测)。 ## 已知限制 - **自定义动态 Bean 无法静态索引**:已支持 `@MapperScan` 与 `META-INF/lazycat/beans`, 但自定义 `BeanDesignImportRegistrar`、`BeansCustomizer`、事件代理、运行时 `Proxy` 等注册的 Bean 仍无法可靠分析。项目外声明的注入类型不会报错,避免误报。 - **Bean 扫描范围由启动类决定**:插件按启动类默认包 / `@BeanScan`、`@Configuration`、`@Import` 和 SPI 重建容器;没有启动类的模块退化为索引项目中所有带 `@Bean`/`@Controller`/`@Service`/`@Configuration` 的类。 条件注解(`@ConditionalOnProperty` 等)按声明存在处理,不做运行时求值。 - **启动扫描只枚举项目源码**:`@BeanScan` / 默认包扫描在运行时还会扫描依赖 jar,插件目前只能对 项目源码执行这一扫描;依赖 jar 中的 Bean 请通过 SPI / `@Import` 声明进入索引。 - 项目源码中未直接声明、仅由自定义 `BeanDesignImportRegistrar` 注册的类无法静态发现 (Pencil 的 `@ToolScan` 工具类同时带 `@Service`,因此仍会被默认包扫描收录)。 - **配置来源**:有效配置值只取自项目中的 `application.yml` / `application.yaml` / `application.properties` 以及 `setting.profiles` 对应的 `application-.*`; `@Value` 跳转的候选声明会额外扫描所有 `application*` 配置文件(含未激活 profile)。 启动参数、系统属性、环境变量、外部 `setting.location`、`VariablesOverwriter` 覆盖无法索引。 - **YAML 解析器是内置的简化实现**:支持嵌套 map、常见的 list-of-map、注释和引号; 不支持锚点/别名、复杂多行块(`|` / `>`)、带索引的列表路径等。 - URL 使用 `127.0.0.1` 作为主机名,端口取 `server.port`(缺省 `8080`),协议由 `server.ssl` 决定。 - 运行时通过代码动态注册的路由(未使用注解声明)不会被发现。 ## 项目结构 ``` src/main/java/xyz/lwm/lazycat/idea/ ├── LazycatCompletionContributor.java # @Inject / @Value / @SettingPrefix / @PathParam 补全 ├── LazycatFileIconProvider.java # 配置文件 / SPI 文件图标 ├── LazycatToolWindowFactory.java # Lazycat 工具窗口 ├── bean/ # Bean 索引、工具、行标记、跳转、检查 ├── config/ # 配置索引/解析、补全/导航支持、检查、意图 ├── model/ # 路由/Bean/配置快照模型 ├── service/ # 项目级模型服务、索引编排、变更监听 ├── spi/ # META-INF/lazycat/beans 解析/加载、行标记、引用 ├── transaction/ # 事务检查 ├── ui/ # 图标、导航、通知工具 ├── util/ # 注解、PSI、路径、包匹配、只读动作工具 └── web/ # 路由索引/工具、行标记、意图、检查、Endpoints 工具窗口 ```