Spring AI 实战:从零开发 IDEA 插件版 AI 代码助手
介绍基于 Spring AI 和 IntelliJ IDEA 插件开发的定制化 AI 代码助手。通过整合 JavaParser 和 Maven API 实现项目上下文感知,支持需求描述生成完整 CRUD 代码、自动修复语法错误及性能优化。文章涵盖架构设计、后端配置、前端插件开发、Prompt 工程、部署发布及常见问题解决方案,提供生产环境可复用的实战代码。

介绍基于 Spring AI 和 IntelliJ IDEA 插件开发的定制化 AI 代码助手。通过整合 JavaParser 和 Maven API 实现项目上下文感知,支持需求描述生成完整 CRUD 代码、自动修复语法错误及性能优化。文章涵盖架构设计、后端配置、前端插件开发、Prompt 工程、部署发布及常见问题解决方案,提供生产环境可复用的实战代码。

作为 Java 开发者,我们每天都在重复编写 CRUD 代码、调试语法错误、优化性能问题。市面上的通用 AI 代码助手往往无法精准感知项目上下文(如包结构、依赖版本、数据库表结构),生成的代码需要大量修改才能落地。
本文基于 Spring AI+IDEA 插件开发了一款定制化 AI 代码助手:后端整合 JavaParser、Maven API 实现代码解析与生成,前端通过 IDEA 插件提供对话窗口和一键插入代码功能,支持需求描述→完整代码生成、代码优化、上下文感知、补全三大核心能力。本文将从实战角度,拆解这款 AI 代码助手的开发全流程。
项目定位:基于 Spring AI 的 Java 代码生成工具,以 IDEA 插件(前端)+ Spring Boot 后端(核心)的架构,解决通用 AI 代码助手上下文脱节、代码适配性差的问题,专注 Java 项目的代码生成、优化与补全。
核心需求:
| 维度 | 核心需求 | 技术挑战 |
|---|---|---|
| 代码生成 | 输入需求描述,生成 Controller+Service+Mapper 完整代码 | Spring AI 精准 Prompt 工程、Java 语法合规性校验 |
| 代码优化 | 自动修复语法错误、优化性能(如 SQL 优化、循环优化) | JavaParser 解析代码 AST、Spring AI 调用大模型分析 |
| 上下文感知 | 感知当前项目的包结构、依赖、数据库表结构 | IDEA 插件获取项目上下文、后端存储上下文信息 |
| 交互体验 | IDEA 内对话窗口、一键插入生成的代码 | IDEA 插件 Swing 开发、前后端通信协议设计 |
以下是 AI 代码助手的核心架构图,清晰呈现前后端交互与核心模块逻辑:

结合 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 代码生成质量高,通义千问支持私有化部署 |
| 存储 | MySQL 8.0 + Redis 7.0 | 存储项目上下文、生成的代码片段 |
首先完成 Spring AI 的基础配置,支持多模型调用(以 OpenAI 为例):
/**
* 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;
}
}
工具调用层是区别于通用 AI 的核心 —— 通过 JavaParser 解析代码 AST、Maven API 解析项目依赖,让 AI 生成的代码贴合项目实际:
/**
* 项目上下文解析服务:基于 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 = (pomFile.getParentFile(), );
(srcDir.exists()) {
context.setSrcRootPath(srcDir.getAbsolutePath());
List<String> packages = parsePackages(srcDir);
context.setPackages(packages);
}
context;
}
List<String> {
List<String> packages = <>();
File[] files = srcDir.listFiles();
(files == ) packages;
(File file : files) {
(file.isDirectory()) {
file.getAbsolutePath().replace(srcDir.getAbsolutePath(), )
.replace(File.separator, );
(!packageName.isEmpty()) {
packages.add(packageName.substring());
}
packages.addAll(parsePackages(file));
}
}
packages;
}
{
{
StaticJavaParser.parse(code);
List<Problem> problems = cu.getProblems();
problems.isEmpty();
} (Exception e) {
;
}
}
}
{
String groupId;
String artifactId;
String basePackage;
List<String> dependencies;
List<String> packages;
String srcRootPath;
String projectId;
}
<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">
<!-- 注册自定义窗口 -->
< = =/>
开发 IDEA 内的对话窗口,支持输入需求、展示生成的代码:
/**
* 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 (resultArea);
generateBtn = ();
insertBtn = ();
insertBtn.setEnabled();
( ());
( ());
topPanel.add( (), BorderLayout.NORTH);
topPanel.add(inputScroll, BorderLayout.CENTER);
();
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);
}
{
collectProjectContext();
();
request.setRequirement(inputArea.getText());
request.setProjectContext(context);
();
resultArea.setText(generatedCode);
insertBtn.setEnabled();
}
ProjectContext {
();
currentProject.getBasePath();
currentProject.getBaseDir().findChild();
(pomFile != ) {
context.setPomPath(pomFile.getPath());
}
FileEditorManager.getInstance(currentProject).getSelectedTextEditor();
(editor != ) {
PsiDocumentManager.getInstance(currentProject).getPsiFile(editor.getDocument());
(psiFile PsiJavaFile) {
(PsiJavaFile) psiFile;
context.setCurrentPackage(javaFile.getPackageName());
}
}
context.setProjectId(currentProject.getName());
context;
}
{
FileEditorManager.getInstance(currentProject).getSelectedTextEditor();
(editor == ) ;
editor.getDocument();
editor.getSelectionModel();
selectionModel.getSelectionStart();
selectionModel.getSelectionEnd();
WriteCommandAction.runWriteCommandAction(currentProject, () -> {
document.replaceString(start, end, resultArea.getText());
});
selectionModel.removeSelection();
editor.getCaretModel().moveToOffset(start + resultArea.getText().length());
}
}
Prompt 工程是代码生成质量的关键 —— 结合项目上下文,让 AI 生成的代码直接贴合项目包结构、依赖版本:
/**
* 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 (formattedSystemPrompt),
(userPrompt)
));
}
}
/**
* 代码生成核心接口
*/
@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);
response.getGeneration().getText();
contextParser.validateJavaCode(generatedCode);
(!isValid) {
generatedCode = regenerateCode(prompt);
}
Result.success(generatedCode);
} (Exception e) {
log.error(, e);
Result.error( + e.getMessage());
}
}
String {
(
prompt.getMessages().stream()
.collect(Collectors.toList())
.add( ())
);
openAiChatClient.generate(newPrompt).getGeneration().getText();
}
}
用户输入需求:生成用户管理的 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);
}
{
userService.delete(id);
}
}
/**
* 构建代码优化的 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)
));
}
/**
* 代码优化接口
*/
@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 >= ) {
response.setOptimizeDesc(parts[].trim());
}
response;
}
原始代码(性能问题):
// 循环查询数据库,性能差
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); // 批量查询
}
优化说明:
上下文感知的代码补全是核心亮点 —— IDEA 插件实时采集当前编辑文件的包结构、已导入的类,后端结合这些信息生成精准的补全建议:
/**
* 代码补全服务:上下文感知的补全建议
*/
@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());
}
}
在 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);
}
CompletableFuture.runAsync(() -> {
List<String> completions = callCompletionApi(request);
showCompletionSuggestions(editor, completions);
});
}
}
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")
}
./gradlew buildPlugin
打包完成后,在 dist 目录下生成 ai-code-assistant-1.0.0.zip 插件包。
http://your-nexus-url/repository/idea-plugins/);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 &
| 问题分类 | 具体问题 | 根因 | 最终解决方案 |
|---|---|---|---|
| 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)) |

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online