跳到主要内容
Spring AI 实战:从零开发 IDEA 插件版 AI 代码助手 | 极客日志
Java AI java
Spring AI 实战:从零开发 IDEA 插件版 AI 代码助手 介绍基于 Spring AI 和 IntelliJ IDEA 插件开发的定制化 AI 代码助手。通过整合 JavaParser 和 Maven API 实现项目上下文感知,支持需求描述生成完整 CRUD 代码、自动修复语法错误及性能优化。文章涵盖架构设计、后端配置、前端插件开发、Prompt 工程、部署发布及常见问题解决方案,提供生产环境可复用的实战代码。
FrontendX 发布于 2026/4/5 更新于 2026/5/24 28 浏览前言
作为 Java 开发者,我们每天都在重复编写 CRUD 代码、调试语法错误、优化性能问题。市面上的通用 AI 代码助手往往无法精准感知项目上下文(如包结构、依赖版本、数据库表结构),生成的代码需要大量修改才能落地。
本文基于 Spring AI+IDEA 插件开发了一款定制化 AI 代码助手:后端整合 JavaParser、Maven API 实现代码解析与生成,前端通过 IDEA 插件提供对话窗口和一键插入代码功能,支持需求描述→完整代码生成、代码优化、上下文感知、补全三大核心能力。本文将从实战角度,拆解这款 AI 代码助手的开发全流程。
一、项目背景与架构设计
1.1 项目定位与核心需求
项目定位 :基于 Spring AI 的 Java 代码生成工具,以 IDEA 插件(前端)+ Spring Boot 后端(核心)的架构,解决通用 AI 代码助手上下文脱节、代码适配性差的问题,专注 Java 项目的代码生成、优化与补全。
核心需求 :
维度 核心需求 技术挑战 代码生成 输入需求描述,生成 Controller+Service+Mapper 完整代码 Spring AI 精准 Prompt 工程、Java 语法合规性校验 代码优化 自动修复语法错误、优化性能(如 SQL 优化、循环优化) JavaParser 解析代码 AST、Spring AI 调用大模型分析 上下文感知 感知当前项目的包结构、依赖、数据库表结构 IDEA 插件获取项目上下文、后端存储上下文信息 交互体验 IDEA 内对话窗口、一键插入生成的代码 IDEA 插件 Swing 开发、前后端通信协议设计
1.2 整体架构设计
以下是 AI 代码助手的核心架构图,清晰呈现前后端交互与核心模块逻辑:
1.3 技术栈选型
结合 Java 生态与 IDEA 插件开发规范,最终选型如下:
技术领域 选型 选型理由 后端核心 Spring Boot 3.2 + Spring AI 0.8.1 Spring 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.0 Swing 实现 IDEA 内窗口,OkHttp 实现前后端通信 大模型 GPT-4 + 通义千问(可选) GPT-4 代码生成质量高,通义千问支持私有化部署 存储
二、核心技术架构拆解
2.1 后端核心:Spring AI + 工具调用体系
2.1.1 Spring AI 核心配置 首先完成 Spring AI 的基础配置,支持多模型调用(以 OpenAI 为例):
@Configuration
public class SpringAiConfig {
@Bean
public OpenAiChatClient openAiChatClient () {
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 工具调用层是区别于通用 AI 的核心 —— 通过 JavaParser 解析代码 AST、Maven API 解析项目依赖,让 AI 生成的代码贴合项目实际:
@Service
public class ProjectContextParser {
public ProjectContext parseMavenProject (String pomPath) throws Exception {
ProjectContext context = new ProjectContext ();
File pomFile = new File (pomPath);
MavenXpp3Reader reader = new MavenXpp3Reader ();
Model model = reader.read(new FileReader (pomFile));
context.setGroupId(model.getGroupId());
context.setArtifactId(model.getArtifactId());
context.setBasePackage(model.getGroupId() + "." + model.getArtifactId());
List<String> dependencies = new ArrayList <>();
for (Dependency dep : model.getDependencies()) {
dependencies.add(dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion());
}
context.setDependencies(dependencies);
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;
}
public boolean validateJavaCode (String code) {
try {
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;
private String artifactId;
private String basePackage;
private List<String> dependencies;
private List<String> packages;
private String srcRootPath;
private String projectId;
}
2.2 前端核心:IDEA 插件开发基础
2.2.1 IDEA 插件工程搭建
创建插件工程 :使用 IntelliJ IDEA 新建 IntelliJ Platform Plugin 工程,选择 SDK 版本(2023.2);
配置 plugin.xml :定义插件入口、窗口布局:
<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 >
<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) 开发 IDEA 内的对话窗口,支持输入需求、展示生成的代码:
public class AiCodeDialog extends JDialog {
private JTextArea inputArea;
private JTextPane resultArea;
private JButton generateBtn;
private JButton insertBtn;
private Project currentProject;
public AiCodeDialog (Project project) {
super (WindowManager.getInstance().getFrame(project), "AI Code Assistant" , Dialog.ModalityType.MODELESS);
this .currentProject = project;
initUI();
setSize(800 , 600 );
setLocationRelativeTo(null );
}
private void initUI () {
inputArea = new JTextArea (5 , 50 );
inputArea.setPlaceholder("请输入代码生成需求,例如:生成用户管理的 Controller+Service+Mapper" );
JScrollPane inputScroll = new JScrollPane (inputArea);
resultArea = new JTextPane ();
resultArea.setContentType("text/java" );
JScrollPane resultScroll = new JScrollPane (resultArea);
generateBtn = new JButton ("生成代码" );
insertBtn = new JButton ("插入到编辑器" );
insertBtn.setEnabled(false );
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);
generateBtn.addActionListener(e -> generateCode());
insertBtn.addActionListener(e -> insertCodeToEditor());
add(panel);
}
private void generateCode () {
ProjectContext context = collectProjectContext();
CodeGenerateRequest request = new CodeGenerateRequest ();
request.setRequirement(inputArea.getText());
request.setProjectContext(context);
OkHttpClient client = new OkHttpClient ();
resultArea.setText(generatedCode);
insertBtn.setEnabled(true );
}
private ProjectContext collectProjectContext () {
ProjectContext context = new ProjectContext ();
String projectPath = currentProject.getBasePath();
VirtualFile pomFile = currentProject.getBaseDir().findChild("pom.xml" );
if (pomFile != null ) {
context.setPomPath(pomFile.getPath());
}
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 工程是代码生成质量的关键 —— 结合项目上下文,让 AI 生成的代码直接贴合项目包结构、依赖版本:
@Service
public class PromptEngineeringService {
public Prompt buildGeneratePrompt (String requirement, ProjectContext context) {
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()));
String userPrompt = "需求:" + requirement;
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 {
ProjectContext context;
if (request.getProjectContext().getProjectId() != null ) {
context = contextRepository.findByProjectId(request.getProjectContext().getProjectId());
if (context == null ) {
context = contextParser.parseMavenProject(request.getProjectContext().getPomPath());
context.setProjectId(request.getProjectContext().getProjectId());
contextRepository.save(context);
}
} else {
context = request.getProjectContext();
}
Prompt prompt = promptService.buildGeneratePrompt(request.getRequirement(), context);
AiResponse response = openAiChatClient.generate(prompt);
String generatedCode = response.getGeneration().getText();
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,包含查询、新增、修改、删除接口
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;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List<User> listAll () {
return userService.listAll();
}
@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 设计
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 {
Prompt prompt = promptService.buildOptimizePrompt(request.getCode(), request.getOptimizeType());
AiResponse response = openAiChatClient.generate(prompt);
String result = response.getGeneration().getText();
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 优化效果示例
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;
}
public List<User> listUsers (List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return Collections.emptyList();
}
return userMapper.listByIds(ids);
}
性能问题:循环遍历 ID 列表,每次查询数据库,导致多次 IO 操作,性能低下;
优化方案:使用 MyBatis 的批量查询方法 listByIds,一次 SQL 查询获取所有数据;
额外优化:增加空值判断,避免空指针异常。
3.3 知识注入:项目上下文感知的代码补全
3.3.1 上下文感知核心逻辑 上下文感知的代码补全是核心亮点 —— IDEA 插件实时采集当前编辑文件的包结构、已导入的类,后端结合这些信息生成精准的补全建议:
@Service
public class CodeCompletionService {
public List<String> completeCode (CodeCompletionRequest request) {
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)
));
AiResponse response = openAiChatClient.generate(prompt);
String result = response.getGeneration().getText();
return Arrays.stream(result.split("\n" ))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
}
3.3.2 IDEA 插件端补全联动 在 IDEA 插件中监听编辑器的输入事件,实时调用补全接口:
public class CodeCompletionListener extends TypedActionHandlerBase {
@Override
public void execute (@NotNull Editor editor, char c, @NotNull DataContext dataContext) {
CaretModel caretModel = editor.getCaretModel();
int offset = caretModel.getOffset();
Document document = editor.getDocument();
String codeSnippet = document.getText(new TextRange (Math.max(0 , offset - 100 ), offset));
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);
}
CompletableFuture.runAsync(() -> {
List<String> completions = callCompletionApi(request);
showCompletionSuggestions(editor, completions);
});
}
}
四、实战部署:插件打包与私有仓库发布
4.1 IDEA 插件打包
配置打包参数 :在 build.gradle 中配置插件打包信息:
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")
}
打包完成后,在 dist 目录下生成 ai-code-assistant-1.0.0.zip 插件包。
4.2 私有仓库发布
搭建私有插件仓库 :使用 Nexus 或 JetBrains Plugin Repository 搭建私有仓库;
上传插件包 :将打包后的 zip 文件上传到私有仓库;
IDEA 配置私有仓库 :
打开 IDEA → Settings → Plugins → Gear 图标 → Manage Plugin Repositories;
添加私有仓库地址(如 http://your-nexus-url/repository/idea-plugins/);
在插件市场中搜索 AI Code Assistant 即可安装。
4.3 后端服务部署 mvn clean package -DskipTests
部署到服务器 :将 target/ai-code-assistant-1.0.0.jar 上传到服务器,执行启动命令:
nohup java -jar ai-code-assistant-1.0.0.jar --spring.profiles.active=prod > app.log 2>&1 &
配置反向代理 :使用 Nginx 配置域名和 HTTPS,对外提供 API 服务。
五、实战踩坑与优化方案 问题分类 具体问题 根因 最终解决方案 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))
六、总结与进阶规划
6.1 核心总结
架构设计 :以 Spring AI 为核心 + 工具调用层为差异化,结合 IDEA 插件实现前后端联动,解决通用 AI 代码助手的上下文脱节问题;
核心能力 :通过 Prompt 工程实现高质量代码生成,基于 JavaParser 实现语法校验,基于 IDEA PSI API 实现上下文采集;
部署落地 :完成插件打包、私有仓库发布、后端服务部署,形成完整的提效工具链;
6.2 进阶规划
私有化部署 :支持通义千问、文心一言等国产大模型私有化部署,满足企业数据安全需求;
本地知识库 :接入项目的数据库表结构、接口文档,进一步提升代码生成的精准度;
批量代码生成 :支持根据数据库表结构,一键生成整个模块的代码;
团队协作 :增加代码片段共享、团队定制 Prompt 功能;
多 IDE 适配 :支持 Eclipse、VS Code 等其他 IDE,扩大使用范围。
相关免费在线工具 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