跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaVScodeAIjava

Spring AI + IDEA 插件:定制化 Java 代码助手实战

基于 Spring AI 和 IDEA 插件实现了一套面向 Java 项目的定制化代码助手。后端结合 Spring Boot、JavaParser、Maven API 解析项目上下文并完成代码生成、语法校验和性能优化,前端通过 IDEA 插件提供对话窗口、结果展示与一键插入代码能力。文章同时展示了 Prompt 设计、代码补全联动、插件打包发布以及实际踩坑后的处理方式,核心结论是:AI 代码助手要真正可用,关键不在模型本身,而在上下文采集、约束和校验这三步。

FrontendX发布于 2026/6/30更新于 2026/8/2318 浏览
Spring AI + IDEA 插件:定制化 Java 代码助手实战

前言

做 Java 开发的人,大多都逃不开重复写 CRUD、改包名、补 import、盯着编译错误一条条修。通用 AI 代码助手当然能提速,但一旦进到具体项目里,包结构、依赖版本、表结构这些上下文对不上,生成结果往往还是要手工重来一遍。

这次我做的是一套偏'项目内可用'的代码助手:后端用 Spring AI 负责模型调用和提示词编排,再配上 JavaParser、Maven API 做项目解析;前端放进 IDEA 插件里,提供对话窗口和一键插入代码。它主要解决三件事:按需求生成完整代码、对已有代码做优化、结合当前项目上下文给出补全建议。

一、项目背景与架构设计

1.1 项目定位与核心需求

这套工具的定位很明确:围绕 Java 项目本身做代码生成,而不是做一个泛用聊天框。前端是 IDEA 插件,后端是 Spring Boot 服务,插件负责采集上下文和交互,后端负责理解需求、拼 Prompt、调模型、做校验。

维度核心需求技术挑战
代码生成输入需求描述,生成 Controller+Service+Mapper 完整代码Spring AI 的 Prompt 设计、Java 语法合规性校验
代码优化自动修复语法错误、优化性能(如 SQL 优化、循环优化)JavaParser 解析代码 AST、Spring AI 调用大模型分析
上下文感知感知当前项目的包结构、依赖、数据库表结构IDEA 插件获取项目上下文、后端存储上下文信息
交互体验IDEA 内对话窗口、一键插入生成的代码IDEA 插件 Swing 开发、前后端通信协议设计

1.2 整体架构设计

文章配图

1.3 技术栈选型

选型基本是顺着 Java 生态往下走,没刻意追新。后端和插件都要稳定,能少踩一点兼容性坑就少踩一点。

技术领域选型选型理由
后端核心Spring Boot 3.2 + Spring AI 0.8.1Spring AI 原生适配 Spring 生态,支持多模型统一调用
代码解析JavaParser 3.25.10轻量、高效的 Java 代码 AST 解析库,支持代码生成 / 校验
Maven 交互Maven API 3.9.6解析项目 pom.xml,获取依赖与包结构
IDEA 插件IntelliJ Platform SDK 2023.2官方 SDK,支持 IDEA 插件全功能开发
前端交互Swing + OkHttp 4.12.0Swing 实现 IDEA 内窗口,OkHttp 实现前后端通信
大模型GPT-4 + 通义千问(可选)GPT-4 代码生成质量高,通义千问支持私有化部署
存储MySQL 8.0 + Redis 7.0存储项目上下文、生成的代码片段

二、核心技术架构拆解

2.1 后端核心:Spring AI + 工具调用体系

2.1.1 Spring AI 核心配置

先把模型客户端接起来,后面的 Prompt、工具调用、结果校验才有落点。

/**
 * Spring AI 核心配置类
 */
@Configuration
public class SpringAiConfig {
    /**
     * 配置 OpenAI 客户端
     */
    @Bean
    public OpenAiChatClient openAiChatClient() {
        // 从配置文件读取 API Key 和 Base URL
        String apiKey = System.getenv("OPENAI_API_KEY");
        String baseUrl = "https://api.openai.com/v1";
        OpenAiApi openAiApi = new OpenAiApi(baseUrl, apiKey);
        // 创建客户端并配置默认参数
        OpenAiChatClient client = new OpenAiChatClient(openAiApi);
        client.setTemperature(0.2); // 低温度保证代码稳定性
        client.setModel("gpt-4");
        return client;
    }
}
2.1.2 工具调用层:JavaParser + Maven API

真正让它和'普通聊天机器人'拉开差距的,是这层上下文解析。光靠模型猜,生成结果很容易飘;把 pom、包结构、源码目录这些信息喂进去,代码才更像是给当前项目现写的。

/**
 * 项目上下文解析服务:基于 Maven API+JavaParser 解析项目信息
 */
@Service
public class ProjectContextParser {
    /**
     * 解析 Maven 项目的包结构、依赖信息
     * @param pomPath pom.xml 文件路径(从 IDEA 插件传递)
     */
    public ProjectContext parseMavenProject(String pomPath) throws Exception {
        ProjectContext context = new ProjectContext();
        // 1. 解析 pom.xml 获取基础信息
        File pomFile = new File(pomPath);
        MavenXpp3Reader reader = new MavenXpp3Reader();
        Model model = reader.read(new FileReader(pomFile));
        // 设置 groupId、artifactId、版本
        context.setGroupId(model.getGroupId());
        context.setArtifactId(model.getArtifactId());
        context.setBasePackage(model.getGroupId() + "." + model.getArtifactId());
        // 2. 解析依赖信息
        List<String> dependencies = new ArrayList<>();
        for (Dependency dep : model.getDependencies()) {
            dependencies.add(dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion());
        }
        context.setDependencies(dependencies);
        // 3. 解析项目源码目录(简化版)
        File srcDir = new File(pomFile.getParentFile(), "src/main/java");
        if (srcDir.exists()) {
            context.setSrcRootPath(srcDir.getAbsolutePath());
            // 解析已存在的包结构
            List<String> packages = parsePackages(srcDir);
            context.setPackages(packages);
        }
        return context;
    }

    /**
     * 解析源码目录下的所有包
     */
    private List<String> parsePackages(File srcDir) {
        List<String> packages = new ArrayList<>();
        // 递归遍历源码目录,解析包名(简化版)
        File[] files = srcDir.listFiles();
        if (files == null) return packages;
        for (File file : files) {
            if (file.isDirectory()) {
                String packageName = file.getAbsolutePath().replace(srcDir.getAbsolutePath(), "")
                        .replace(File.separator, ".");
                if (!packageName.isEmpty()) {
                    packages.add(packageName.substring(1)); // 去掉开头的.
                }
                packages.addAll(parsePackages(file));
            }
        }
        return packages;
    }

    /**
     * 解析 Java 代码 AST,校验语法正确性
     */
    public boolean validateJavaCode(String code) {
        try {
            // 使用 JavaParser 解析代码,检测语法错误
            CompilationUnit cu = StaticJavaParser.parse(code);
            List<Problem> problems = cu.getProblems();
            return problems.isEmpty();
        } catch (Exception e) {
            return false;
        }
    }
}

// 项目上下文实体类
@Data
public class ProjectContext {
    private String groupId; // 项目 groupId
    private String artifactId; // 项目 artifactId
    private String basePackage; // 基础包名
    private List<String> dependencies; // 依赖列表
    private List<String> packages; // 包结构
    private String srcRootPath; // 源码根路径
    private String projectId; // IDEA 插件传递的项目唯一标识
}

2.2 前端核心:IDEA 插件开发基础

2.2.1 IDEA 插件工程搭建

插件侧没有做太多花活,先把入口、窗口和快捷键跑通。IDEA 插件开发最容易浪费时间的地方,不是业务逻辑,而是各种生命周期、上下文和 UI 线程问题。

<idea-plugin>
    <id>com.ai.code.assistant</id>
    <name>AI Code Assistant</name>
    <version>1.0</version>
    <vendor email="[email protected]" url="https://your.site">Your Name</vendor>
    <description>
        基于 Spring AI 的 Java 代码助手,支持上下文感知的代码生成与优化
    </description>
    <!-- 注册 Action,用于打开对话窗口 -->
    <actions>
        <action text="AI Code Assistant" description="Open AI Code Assistant Dialog">
            <add-to-group group-id="EditorPopupMenu" anchor="first"/>
            <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt A"/>
        </action>
    </actions>
    <extensions defaultExtensionNs="com.intellij">
        <!-- 注册自定义窗口 -->
        <toolWindow anchor="right" factoryClass="com.ai.code.assistant.window.AiToolWindowFactory"/>
    </extensions>
</idea-plugin>
2.2.2 对话窗口开发(Swing)

窗口本身不复杂,重点是:别阻塞编辑器,别让插入代码时把当前光标位置和选区搞乱。

/**
 * AI 代码助手对话窗口
 */
public class AiCodeDialog extends JDialog {
    private JTextArea inputArea; // 需求输入框
    private JTextPane resultArea; // 代码结果展示框
    private JButton generateBtn; // 生成按钮
    private JButton insertBtn; // 插入代码按钮
    private Project currentProject; // 当前 IDEA 项目

    public AiCodeDialog(Project project) {
        super(WindowManager.getInstance().getFrame(project), "AI Code Assistant", Dialog.ModalityType.MODELESS);
        this.currentProject = project;
        initUI(); // 初始化 UI
        setSize(800, 600);
        setLocationRelativeTo(null);
    }

    /**
     * 初始化 UI 组件
     */
    private void initUI() {
        // 1. 输入区域
        inputArea = new JTextArea(5, 50);
        inputArea.setPlaceholder("请输入代码生成需求,例如:生成用户管理的 Controller+Service+Mapper");
        JScrollPane inputScroll = new JScrollPane(inputArea);
        // 2. 结果展示区域(支持语法高亮)
        resultArea = new JTextPane();
        resultArea.setContentType("text/java");
        JScrollPane resultScroll = new JScrollPane(resultArea);
        // 3. 按钮区域
        generateBtn = new JButton("生成代码");
        insertBtn = new JButton("插入到编辑器");
        insertBtn.setEnabled(false); // 初始禁用
        // 4. 布局
        JPanel panel = new JPanel(new BorderLayout());
        JPanel topPanel = new JPanel(new BorderLayout());
        topPanel.add(new JLabel("需求描述:"), BorderLayout.NORTH);
        topPanel.add(inputScroll, BorderLayout.CENTER);
        JPanel btnPanel = new JPanel();
        btnPanel.add(generateBtn);
        btnPanel.add(insertBtn);
        panel.add(topPanel, BorderLayout.NORTH);
        panel.add(resultScroll, BorderLayout.CENTER);
        panel.add(btnPanel, BorderLayout.SOUTH);
        // 5. 绑定事件
        generateBtn.addActionListener(e -> generateCode());
        insertBtn.addActionListener(e -> insertCodeToEditor());
        add(panel);
    }

    /**
     * 生成代码:调用后端接口
     */
    private void generateCode() {
        // 1. 采集项目上下文
        ProjectContext context = collectProjectContext();
        // 2. 构建请求参数
        CodeGenerateRequest request = new CodeGenerateRequest();
        request.setRequirement(inputArea.getText());
        request.setProjectContext(context);
        // 3. 调用后端接口(OkHttp 实现,略)
        OkHttpClient client = new OkHttpClient();
        // ... 发送 POST 请求,获取生成的代码
        // 4. 展示结果
        resultArea.setText(generatedCode);
        insertBtn.setEnabled(true);
    }

    /**
     * 采集当前 IDEA 项目的上下文
     */
    private ProjectContext collectProjectContext() {
        ProjectContext context = new ProjectContext();
        // 1. 获取项目根路径
        String projectPath = currentProject.getBasePath();
        // 2. 获取 pom.xml 路径(Maven 项目)
        VirtualFile pomFile = currentProject.getBaseDir().findChild("pom.xml");
        if (pomFile != null) {
            context.setPomPath(pomFile.getPath());
        }
        // 3. 获取当前编辑器的包路径
        Editor editor = FileEditorManager.getInstance(currentProject).getSelectedTextEditor();
        if (editor != null) {
            PsiFile psiFile = PsiDocumentManager.getInstance(currentProject).getPsiFile(editor.getDocument());
            if (psiFile instanceof PsiJavaFile) {
                PsiJavaFile javaFile = (PsiJavaFile) psiFile;
                context.setCurrentPackage(javaFile.getPackageName());
            }
        }
        context.setProjectId(currentProject.getName());
        return context;
    }

    /**
     * 将生成的代码插入到当前编辑器
     */
    private void insertCodeToEditor() {
        Editor editor = FileEditorManager.getInstance(currentProject).getSelectedTextEditor();
        if (editor == null) return;
        // 获取选中区域,无选中则插入到光标位置
        Document document = editor.getDocument();
        SelectionModel selectionModel = editor.getSelectionModel();
        int start = selectionModel.getSelectionStart();
        int end = selectionModel.getSelectionEnd();
        // 写入代码
        WriteCommandAction.runWriteCommandAction(currentProject, () -> {
            document.replaceString(start, end, resultArea.getText());
        });
        // 取消选中,光标移到末尾
        selectionModel.removeSelection();
        editor.getCaretModel().moveToOffset(start + resultArea.getText().length());
    }
}

三、核心功能实现

3.1 代码生成:Controller+Service+Mapper 完整生成

3.1.1 Prompt 工程核心逻辑

Prompt 不只是'把需求塞给模型'这么简单。要让它知道项目基础包、已有包、依赖版本,还要把'只返回代码'这类约束写得足够硬,不然后面处理结果会很烦。

/**
 * Prompt 工程服务:动态拼接上下文,生成高质量 Prompt
 */
@Service
public class PromptEngineeringService {
    /**
     * 生成代码生成的 Prompt
     * @param requirement 用户需求
     * @param context 项目上下文
     */
    public Prompt buildGeneratePrompt(String requirement, ProjectContext context) {
        // 1. 构建系统提示词(核心规则)
        String systemPrompt = "你是一位资深 Java 后端开发工程师,精通 Spring Boot、MyBatis、MySQL。\n" +
                "请根据以下需求和项目上下文,生成符合规范的 Java 代码:\n" +
                "1. 包结构必须符合项目基础包:%s\n" +
                "2. 代码必须兼容项目依赖版本,优先使用项目已引入的依赖\n" +
                "3. 生成完整的 Controller+Service+Mapper 层,包含必要的注释、异常处理\n" +
                "4. 代码风格符合阿里巴巴 Java 开发手册\n" +
                "5. 只返回代码,不返回多余解释\n" +
                "项目上下文:\n" +
                " - 基础包名:%s\n" +
                " - 已存在的包:%s\n" +
                " - 项目依赖:%s";
        String formattedSystemPrompt = String.format(systemPrompt,
                context.getBasePackage(),
                context.getBasePackage(),
                String.join(",", context.getPackages()),
                String.join(",", context.getDependencies()));
        // 2. 构建用户提示词
        String userPrompt = "需求:" + requirement;
        // 3. 创建 Prompt 对象
        return new Prompt(List.of(
                new SystemMessage(formattedSystemPrompt),
                new UserMessage(userPrompt)
        ));
    }
}
3.1.2 代码生成核心接口
/**
 * 代码生成核心接口
 */
@RestController
@RequestMapping("/api/code")
public class CodeGenerateController {
    @Autowired
    private OpenAiChatClient openAiChatClient;
    @Autowired
    private PromptEngineeringService promptService;
    @Autowired
    private ProjectContextParser contextParser;
    @Autowired
    private ProjectContextRepository contextRepository;

    /**
     * 生成代码接口
     */
    @PostMapping("/generate")
    public Result<String> generateCode(@RequestBody CodeGenerateRequest request) {
        try {
            // 1. 解析项目上下文(如果未存储)
            ProjectContext context;
            if (request.getProjectContext().getProjectId() != null) {
                context = contextRepository.findByProjectId(request.getProjectContext().getProjectId());
                if (context == null) {
                    // 解析 pom.xml 生成上下文
                    context = contextParser.parseMavenProject(request.getProjectContext().getPomPath());
                    context.setProjectId(request.getProjectContext().getProjectId());
                    contextRepository.save(context); // 存储上下文
                }
            } else {
                context = request.getProjectContext();
            }
            // 2. 构建 Prompt
            Prompt prompt = promptService.buildGeneratePrompt(request.getRequirement(), context);
            // 3. 调用 Spring AI 生成代码
            AiResponse response = openAiChatClient.generate(prompt);
            String generatedCode = response.getGeneration().getText();
            // 4. 校验代码语法
            boolean isValid = contextParser.validateJavaCode(generatedCode);
            if (!isValid) {
                // 重新生成(简化版,实际可加入重试逻辑)
                generatedCode = regenerateCode(prompt);
            }
            return Result.success(generatedCode);
        } catch (Exception e) {
            log.error("生成代码失败", e);
            return Result.error("生成代码失败:" + e.getMessage());
        }
    }

    /**
     * 代码校验失败时重新生成
     */
    private String regenerateCode(Prompt prompt) {
        // 追加提示词,要求修复语法错误
        Prompt newPrompt = new Prompt(
                prompt.getMessages().stream()
                        .collect(Collectors.toList())
                        .add(new UserMessage("以上代码存在语法错误,请修复后重新生成,只返回修复后的代码"))
        );
        return openAiChatClient.generate(newPrompt).getGeneration().getText();
    }
}
3.1.3 效果示例

用户输入需求:生成用户管理的 Controller+Service+Mapper,包含查询、新增、修改、删除接口。

生成的代码(Controller 层):

package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

/**
 * 用户管理控制器
 * @author AI Code Assistant
 * @date 2024-05-20
 */
@RestController
@RequestMapping("/api/user")
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 查询所有用户
     */
    @GetMapping
    public List<User> listAll() {
        return userService.listAll();
    }

    /**
     * 根据 ID 查询用户
     */
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id) {
        return userService.getById(id);
    }

    /**
     * 新增用户
     */
    @PostMapping
    public boolean save(@RequestBody User user) {
        return userService.save(user);
    }

    /**
     * 修改用户
     */
    @PutMapping("/{id}")
    public boolean update(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        return userService.update(user);
    }

    /**
     * 删除用户
     */
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Long id) {
        return userService.delete(id);
    }
}

3.2 代码优化:自动修复语法错误与性能问题

3.2.1 代码优化 Prompt 设计

优化这块的重点不是'写得更漂亮',而是先把最常见的问题解决掉:能编译、逻辑不变、性能有实质改善。

/**
 * 构建代码优化的 Prompt
 */
public Prompt buildOptimizePrompt(String code, String optimizeType) {
    String systemPrompt = "你是一位资深 Java 性能优化工程师,精通 Java 语法、性能调优。\n" +
            "请根据指定类型优化以下代码:\n" +
            "优化类型:%s\n" +
            "优化规则:\n" +
            "1. 修复语法错误,保证代码可编译\n" +
            "2. 性能优化需给出具体的优化点(如循环优化、SQL 优化、集合使用优化)\n" +
            "3. 保留原有业务逻辑,只优化语法和性能\n" +
            "4. 输出优化后的代码 + 优化说明(分开展示)";
    String formattedSystemPrompt = String.format(systemPrompt, optimizeType);
    return new Prompt(List.of(
            new SystemMessage(formattedSystemPrompt),
            new UserMessage("需要优化的代码:\n" + code)
    ));
}
3.2.2 代码优化接口实现
/**
 * 代码优化接口
 */
@PostMapping("/optimize")
public Result<CodeOptimizeResponse> optimizeCode(@RequestBody CodeOptimizeRequest request) {
    try {
        // 1. 构建优化 Prompt
        Prompt prompt = promptService.buildOptimizePrompt(request.getCode(), request.getOptimizeType());
        // 2. 调用 AI 优化代码
        AiResponse response = openAiChatClient.generate(prompt);
        String result = response.getGeneration().getText();
        // 3. 解析优化结果(代码 + 说明)
        CodeOptimizeResponse responseVO = parseOptimizeResult(result);
        return Result.success(responseVO);
    } catch (Exception e) {
        log.error("优化代码失败", e);
        return Result.error("优化代码失败:" + e.getMessage());
    }
}

/**
 * 解析优化结果(简化版,实际可按固定格式解析)
 */
private CodeOptimizeResponse parseOptimizeResult(String result) {
    CodeOptimizeResponse response = new CodeOptimizeResponse();
    // 假设结果以"===优化后代码==="和"===优化说明==="分隔
    String[] parts = result.split("===优化说明===");
    if (parts.length >= 1) {
        response.setOptimizedCode(parts[0].replace("===优化后代码===" , "").trim());
    }
    if (parts.length >= 2) {
        response.setOptimizeDesc(parts[1].trim());
    }
    return response;
}
3.2.3 优化效果示例

原始代码的毛病很典型:把数据库查询放在循环里,数据一多,IO 次数就直接上去了。

// 循环查询数据库,性能差
public List<User> listUsers(List<Long> ids) {
    List<User> users = new ArrayList<>();
    for (Long id : ids) {
        User user = userMapper.getById(id); // 每次循环查库
        users.add(user);
    }
    return users;
}

优化后代码:

// 批量查询数据库,减少 IO 次数
public List<User> listUsers(List<Long> ids) {
    // 优化点:批量查询替代循环查询,减少数据库连接次数
    if (CollectionUtils.isEmpty(ids)) {
        return Collections.emptyList();
    }
    return userMapper.listByIds(ids); // 批量查询
}

优化说明:

  1. 性能问题:循环遍历 ID 列表,每次查询数据库,导致多次 IO 操作,性能低下;
  2. 优化方案:使用 MyBatis 的批量查询方法 listByIds,一次 SQL 查询获取所有数据;
  3. 额外优化:增加空值判断,避免空指针异常。

3.3 知识注入:项目上下文感知的代码补全

3.3.1 上下文感知核心逻辑

补全场景比生成完整代码更挑上下文。当前包名、已导入类、光标前的片段,缺一个都容易跑偏。所以这里的策略是尽量把编辑器里能拿到的信息都收进来,再让模型去补。

/**
 * 代码补全服务:上下文感知的补全建议
 */
@Service
public class CodeCompletionService {
    /**
     * 生成上下文感知的代码补全建议
     */
    public List<String> completeCode(CodeCompletionRequest request) {
        // 1. 构建补全 Prompt
        String systemPrompt = "请根据当前 Java 文件的上下文,生成代码补全建议:\n" +
                "1. 补全建议必须符合当前包结构:%s\n" +
                "2. 优先使用已导入的类:%s\n" +
                "3. 补全建议简洁,每条不超过 50 个字符\n" +
                "4. 只返回补全建议列表,每行一个";
        String formattedSystemPrompt = String.format(systemPrompt,
                request.getCurrentPackage(),
                String.join(",", request.getImportedClasses()));
        String userPrompt = "需要补全的代码片段:\n" + request.getCodeSnippet();
        Prompt prompt = new Prompt(List.of(
                new SystemMessage(formattedSystemPrompt),
                new UserMessage(userPrompt)
        ));
        // 2. 调用 AI 生成补全建议
        AiResponse response = openAiChatClient.generate(prompt);
        String result = response.getGeneration().getText();
        // 3. 解析补全建议列表
        return Arrays.stream(result.split("\n"))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toList());
    }
}
3.3.2 IDEA 插件端补全联动
/**
 * 代码补全监听器:监听编辑器输入,实时调用补全接口
 */
public class CodeCompletionListener extends TypedActionHandlerBase {
    @Override
    public void execute(@NotNull Editor editor, char c, @NotNull DataContext dataContext) {
        // 1. 采集当前代码片段(光标前 100 个字符)
        CaretModel caretModel = editor.getCaretModel();
        int offset = caretModel.getOffset();
        Document document = editor.getDocument();
        String codeSnippet = document.getText(new TextRange(Math.max(0, offset - 100), offset));
        // 2. 采集上下文(当前包、已导入的类)
        Project project = CommonDataKeys.PROJECT.getData(dataContext);
        PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
        CodeCompletionRequest request = new CodeCompletionRequest();
        request.setCodeSnippet(codeSnippet);
        if (psiFile instanceof PsiJavaFile) {
            PsiJavaFile javaFile = (PsiJavaFile) psiFile;
            request.setCurrentPackage(javaFile.getPackageName());
            // 获取已导入的类
            List<String> importedClasses = javaFile.getImportList().getAllImports().stream()
                    .map(ImportStatement::getQualifiedName)
                    .collect(Collectors.toList());
            request.setImportedClasses(importedClasses);
        }
        // 3. 调用后端补全接口(异步,避免阻塞编辑器)
        CompletableFuture.runAsync(() -> {
            List<String> completions = callCompletionApi(request);
            // 4. 在编辑器中展示补全建议(使用 IDEA 的 CompletionContributor)
            showCompletionSuggestions(editor, completions);
        });
    }
}

四、实战部署:插件打包与私有仓库发布

4.1 IDEA 插件打包

Gradle 这部分没什么特别的,关键是版本别乱跳,IDEA SDK 和插件依赖一变,后面兼容问题会很烦。

plugins {
    id 'java'
    id 'org.jetbrains.intellij' version '1.17.3'
}
intellij {
    version = '2023.2'
    type = 'IC'
    plugins = ['java']
}
sourceCompatibility = 17
targetCompatibility = 17
// 打包配置
tasks.buildPlugin {
    archiveBaseName = 'ai-code-assistant'
    archiveVersion = '1.0.0'
    destinationDirectory = file("$projectDir/dist")
}
./gradlew buildPlugin

打完包后,会在 dist 目录里生成 ai-code-assistant-1.0.0.zip。

4.2 私有仓库发布

如果是团队内用,放到私有仓库比手工发包省事得多。Nexus 或 JetBrains Plugin Repository 都能用,流程差不多。

  1. 搭建私有插件仓库;
  2. 上传插件包;
  3. 在 IDEA 里添加仓库地址:http://your-nexus-url/repository/idea-plugins/。

4.3 后端服务部署

mvn clean package -DskipTests
nohup java -jar ai-code-assistant-1.0.0.jar --spring.profiles.active=prod > app.log 2>&1 &

线上服务还是走常规套路:打包、部署、Nginx 反代,没什么新鲜的,但足够稳。

五、实战踩坑与优化方案

问题分类具体问题根因最终解决方案
IDEA 插件插件启动时获取不到项目上下文插件加载时机过早,项目未完全初始化在 projectOpened 事件中初始化上下文采集逻辑
代码生成AI 生成的代码包名错误Prompt 中上下文拼接不完整优化 Prompt,强制 AI 使用项目基础包名,增加校验逻辑
性能问题代码生成响应慢(>5s)AI 调用 + 上下文解析耗时1. 缓存项目上下文(Redis);2. 异步生成代码,返回任务 ID 轮询结果
语法校验JavaParser 校验误判JavaParser 版本与 IDEA SDK 不兼容统一使用 IDEA 内置的 Java 解析器(PSI API)替代 JavaParser
插件交互插入代码时格式错乱换行符 / 缩进不一致插入前格式化代码(CodeStyleManager.getInstance(project).reformat(psiElement))

六、总结与进阶规划

这套东西做下来,最直接的感受是:生成能力本身不是难点,难的是让模型输出的东西真的能贴到项目里用。上下文采集、Prompt 约束、语法校验、插入编辑器这几步,哪一步松一点,最后都要靠人工兜底。

后面如果继续做,我会优先补这几块:私有化模型接入、本地知识库、按表结构批量生成模块代码,还有团队内共享 Prompt 的能力。多 IDE 适配也能做,但那是后面的事了,先把 IDEA 这条链路打稳更实际。

目录

  1. 前言
  2. 一、项目背景与架构设计
  3. 1.1 项目定位与核心需求
  4. 1.2 整体架构设计
  5. 1.3 技术栈选型
  6. 二、核心技术架构拆解
  7. 2.1 后端核心:Spring AI + 工具调用体系
  8. 2.1.1 Spring AI 核心配置
  9. 2.1.2 工具调用层:JavaParser + Maven API
  10. 2.2 前端核心:IDEA 插件开发基础
  11. 2.2.1 IDEA 插件工程搭建
  12. 2.2.2 对话窗口开发(Swing)
  13. 三、核心功能实现
  14. 3.1 代码生成:Controller+Service+Mapper 完整生成
  15. 3.1.1 Prompt 工程核心逻辑
  16. 3.1.2 代码生成核心接口
  17. 3.1.3 效果示例
  18. 3.2 代码优化:自动修复语法错误与性能问题
  19. 3.2.1 代码优化 Prompt 设计
  20. 3.2.2 代码优化接口实现
  21. 3.2.3 优化效果示例
  22. 3.3 知识注入:项目上下文感知的代码补全
  23. 3.3.1 上下文感知核心逻辑
  24. 3.3.2 IDEA 插件端补全联动
  25. 四、实战部署:插件打包与私有仓库发布
  26. 4.1 IDEA 插件打包
  27. 4.2 私有仓库发布
  28. 4.3 后端服务部署
  29. 五、实战踩坑与优化方案
  30. 六、总结与进阶规划
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Neo4j 安装与使用教程
  • Windows 系统 Docker Desktop 下载、安装及启动故障解决方案
  • Python SQLAlchemy ORM 数据库操作指南
  • Java static 关键字:静态与非静态访问规则详解
  • C++ STL Vector 容器封装与迭代器失效处理
  • Elasticsearch 索引删除操作指南
  • CentOS 7 安装 JDK 8(jdk-8u461-linux-x64)教程
  • CCF-GESP 2025 年 12 月 C++ 二级认证真题解析
  • LeetCode 169/179/215/229 算法题解:多数元素、最大数与 K 大
  • Mac 应用安装提示已损坏的三种解决方法
  • 在 IntelliJ IDEA 中使用飞算 AI 的实操记录
  • OpenClaw 多飞书机器人配置指南
  • 智能体工作流导出与导入:基于 12345 政务热线分拨场景
  • 机器人 URDF 建模与 Isaac Sim 仿真集成指南
  • Claude Code 本地部署与项目实战详解
  • SpringBoot3 + OpenSpec 实现 MCP 服务器实践
  • Python 自适应大邻域搜索(ALNS)算法教程
  • 红黑树(RB-tree)核心原理与 C++ 实现
  • DeepSeek-R1 大模型基于 MS-Swift 框架部署、推理与微调实战
  • Obsidian Copilot API 密钥配置指南:OpenRouter、Gemini、OpenAI
  • 相关免费在线工具

    • Keycode 信息

      查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

    • Escape 与 Native 编解码

      JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

    • JavaScript / HTML 格式化

      使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

    • JavaScript 压缩与混淆

      Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

    • RSA密钥对生成器

      生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

    • Mermaid 预览与可视化编辑

      基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online