做后端开发的朋友应该都有过这种噩梦:前端天天追着要接口文档,手写Swagger注解写到吐,文档刚写完代码又改了,Mock数据还要自己编一堆假名字假手机号,费时费力还容易错。上个月我实在忍无可忍,把GPT-4和文心一言集成到了我们的Spring Boot项目里,不仅能自动生成Swagger注解、补全接口文档,还能根据返回值类型生成真实的Mock数据,前端现在直接用Swagger UI就能调接口,再也不来烦我了。

今天就把这套方案的全流程实现、踩过的坑、性能优化全部分享给你们,帮你们把写文档的时间省下来写业务代码。

一、为什么非要自己集成AI?第三方工具不香吗?

先说说传统接口文档方案的痛点:

  1. 手写Swagger注解太累:每个接口都要写@ApiOperation、@ApiParam、@ApiModel,100个接口就要写几百行注解,代码看着乱,维护起来更麻烦;
  2. 文档和代码不同步:代码改了参数或返回值,文档忘了更新,前端按文档调接口报错,互相甩锅;
  3. Mock数据太假:自己写的Mock数据不是“test123”就是“张三”,前端测试时总说数据不真实,影响开发效率;
  4. 第三方工具不安全:用第三方AI文档工具,要把接口代码传上去,企业级项目根本不敢用,怕泄露业务逻辑。

自己集成Spring Boot + AI大模型的优势刚好解决这些问题:

  • 无缝集成现有项目:不用额外部署服务,直接在项目里加个依赖就能用,和SpringDoc/Swagger完美结合;
  • 文档代码自动同步:每次启动项目时自动扫描Controller,AI重新生成注解和文档,永远同步;
  • Mock数据真实可信:AI能根据字段名生成真实的姓名、手机号、地址,甚至能生成符合业务逻辑的订单数据;
  • 数据安全可控:敏感信息可以脱敏后再传给AI,或者用文心一言的私有化部署,数据完全不出去。

二、环境准备:版本选对,少踩一半坑

我用的版本都是经过验证的,兼容性和稳定性最好:

  • JDK:17(Spring Boot 3.x要求,AI SDK也支持)
  • Spring Boot:3.2.5
  • SpringDoc:2.3.0(比Swagger更现代,更容易扩展)
  • OpenAI Java SDK:0.18.0(调用GPT-4用)
  • 百度千帆SDK:0.2.0(调用文心一言用,国内项目推荐)
  • Maven:3.9.6

2.1 Maven依赖配置

先在pom.xml里加必要的依赖:

<properties>
    <java.version>17</java.version>
    <spring-boot.version>3.2.5</spring-boot.version>
    <springdoc.version>2.3.0</springdoc.version>
    <openai.version>0.18.0</openai.version>
    <qianfan.version>0.2.0</qianfan.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
</parent>

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- SpringDoc OpenAPI(替代Swagger) -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.3.0</version>
    </dependency>

    <!-- OpenAI SDK(调用GPT-4) -->
    <dependency>
        <groupId>com.theokanning.openai-gpt3-java</groupId>
        <artifactId>service</artifactId>
        <version>${openai.version}</version>
    </dependency>

    <!-- 百度千帆SDK(调用文心一言) -->
    <dependency>
        <groupId>com.baidubce</groupId>
        <artifactId>qianfan</artifactId>
        <version>${qianfan.version}</version>
    </dependency>

    <!-- 缓存用Caffeine,避免频繁调用AI -->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>

    <!-- JSON处理 -->
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.43</version>
    </dependency>
</dependencies>

2.2 大模型配置

在application.yml里配置大模型的API Key和参数:

spring:
  application:
    name: ai-doc-generator
  ai:
    # 选择用哪个大模型:gpt-4 或 wenxin
    provider: wenxin
    gpt:
      api-key: sk-your-openai-api-key
      model: gpt-4-turbo
      temperature: 0.3
      max-tokens: 2000
    wenxin:
      api-key: your-qianfan-api-key
      secret-key: your-qianfan-secret-key
      model: ERNIE-4.0-8K
      temperature: 0.3
      max-tokens: 2000
  # Caffeine缓存配置,缓存AI生成的文档和Mock数据
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=1000,expireAfterWrite=1h

# SpringDoc配置
springdoc:
  api-docs:
    path: /v3/api-docs
  swagger-ui:
    path: /swagger-ui.html
    enabled: true

三、核心模块实现:从自动生成注解到智能Mock

我把系统分成了三个核心模块:Swagger注解自动生成模块、智能接口文档增强模块、Mock数据自动生成模块,每个模块都踩了不少坑。

3.1 模块一:Swagger注解自动生成,再也不用手写

这个模块的核心逻辑是:项目启动时,用反射扫描所有Controller类,提取方法名、参数、返回值类型,构建Prompt传给AI,让AI生成@Tag、@Operation、@Parameter这些注解的内容,然后动态生成OpenAPI文档,不用在代码里写任何注解。

核心实现代码

先写一个AI服务接口,支持GPT-4和文心一言切换:

public interface AiService {
    String generate(String prompt);
}

// GPT-4实现
@Service
@ConditionalOnProperty(name = "spring.ai.provider", havingValue = "gpt")
public class GptAiService implements AiService {
    @Value("${spring.ai.gpt.api-key}")
    private String apiKey;
    @Value("${spring.ai.gpt.model}")
    private String model;
    @Value("${spring.ai.gpt.temperature}")
    private double temperature;
    @Value("${spring.ai.gpt.max-tokens}")
    private int maxTokens;

    private OpenAiService openAiService;

    @PostConstruct
    public void init() {
        openAiService = new OpenAiService(apiKey);
    }

    @Override
    public String generate(String prompt) {
        ChatCompletionRequest request = ChatCompletionRequest.builder()
                .model(model)
                .messages(List.of(new ChatMessage("user", prompt)))
                .temperature(temperature)
                .maxTokens(maxTokens)
                .build();
        return openAiService.createChatCompletion(request).getChoices().get(0).getMessage().getContent();
    }
}

// 文心一言实现
@Service
@ConditionalOnProperty(name = "spring.ai.provider", havingValue = "wenxin")
public class WenxinAiService implements AiService {
    @Value("${spring.ai.wenxin.api-key}")
    private String apiKey;
    @Value("${spring.ai.wenxin.secret-key}")
    private String secretKey;
    @Value("${spring.ai.wenxin.model}")
    private String model;
    @Value("${spring.ai.wenxin.temperature}")
    private double temperature;
    @Value("${spring.ai.wenxin.max-tokens}")
    private int maxTokens;

    private QianfanClient qianfanClient;

    @PostConstruct
    public void init() {
        qianfanClient = new QianfanClient(apiKey, secretKey);
    }

    @Override
    public String generate(String prompt) {
        ChatRequest request = ChatRequest.builder()
                .model(model)
                .messages(List.of(new ChatMessage("user", prompt)))
                .temperature(temperature)
                .maxOutputTokens(maxTokens)
                .build();
        return qianfanClient.chat(request).getResult();
    }
}

然后写一个OpenAPI自定义配置类,扫描Controller,用AI生成文档:

@Configuration
public class OpenApiConfig {
    @Autowired
    private AiService aiService;
    @Autowired
    private CacheManager cacheManager;

    @Bean
    public OpenAPI customOpenAPI() {
        OpenAPI openAPI = new OpenAPI()
                .info(new Info()
                        .title("智能监控系统API文档")
                        .version("1.0.0")
                        .description("由AI自动生成的接口文档"));
        // 扫描所有Controller,生成Paths
        openAPI.setPaths(generatePaths());
        return openAPI;
    }

    private Paths generatePaths() {
        Paths paths = new Paths();
        // 用Spring的工具类扫描所有Controller类
        List<Class<?>> controllerClasses = ClassPathScanningCandidateComponentProvider
                .forDefaultResourcePatterns()
                .findCandidateComponents("com.example.monitoring.controller")
                .stream()
                .map(bd -> {
                    try {
                        return Class.forName(bd.getBeanClassName());
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                })
                .toList();

        for (Class<?> controllerClass : controllerClasses) {
            // 生成Controller的@Tag
            Tag tag = generateTag(controllerClass);
            paths.addTagsItem(tag);
            // 扫描Controller的所有方法
            for (Method method : controllerClass.getDeclaredMethods()) {
                if (method.isAnnotationPresent(RequestMapping.class) ||
                        method.isAnnotationPresent(GetMapping.class) ||
                        method.isAnnotationPresent(PostMapping.class)) {
                    // 生成方法的PathItem
                    PathItem pathItem = generatePathItem(method, tag.getName());
                    // 获取请求路径
                    String path = getPath(method, controllerClass);
                    paths.addPathItem(path, pathItem);
                }
            }
        }
        return paths;
    }

    private Tag generateTag(Class<?> controllerClass) {
        String cacheKey = "tag:" + controllerClass.getName();
        Cache cache = cacheManager.getCache("ai-doc");
        if (cache.get(cacheKey) != null) {
            return (Tag) cache.get(cacheKey).get();
        }
        // 构建Prompt,让AI生成Controller的描述
        String prompt = String.format("""
                请为这个Java Controller类生成一个OpenAPI的Tag,包含name和description。
                类名:%s
                类上的注解:%s
                请只返回JSON格式,不要有其他文字,格式如下:
                {"name": "xxx", "description": "xxx"}
                """, controllerClass.getSimpleName(), Arrays.toString(controllerClass.getAnnotations()));
        String aiResponse = aiService.generate(prompt);
        Tag tag = JSON.parseObject(aiResponse, Tag.class);
        cache.put(cacheKey, tag);
        return tag;
    }

    private PathItem generatePathItem(Method method, String tagName) {
        String cacheKey = "path:" + method.getDeclaringClass().getName() + ":" + method.getName();
        Cache cache = cacheManager.getCache("ai-doc");
        if (cache.get(cacheKey) != null) {
            return (PathItem) cache.get(cacheKey).get();
        }
        // 构建Prompt,让AI生成接口的Operation
        String prompt = String.format("""
                请为这个Java接口方法生成OpenAPI的Operation,包含summary、description、parameters、requestBody、responses。
                方法名:%s
                方法参数:%s
                方法返回值:%s
                方法上的注解:%s
                请只返回JSON格式,不要有其他文字,格式如下:
                {"summary": "xxx", "description": "xxx", "parameters": [...], "requestBody": {...}, "responses": {...}}
                """, method.getName(), Arrays.toString(method.getParameters()),
                method.getReturnType().getName(), Arrays.toString(method.getAnnotations()));
        String aiResponse = aiService.generate(prompt);
        Operation operation = JSON.parseObject(aiResponse, Operation.class);
        operation.addTagsItem(tagName);
        // 根据请求方法设置PathItem
        PathItem pathItem = new PathItem();
        if (method.isAnnotationPresent(GetMapping.class)) {
            pathItem.setGet(operation);
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            pathItem.setPost(operation);
        }
        cache.put(cacheKey, pathItem);
        return pathItem;
    }

    // 辅助方法:获取请求路径,省略具体实现
    private String getPath(Method method, Class<?> controllerClass) {
        // 解析类上的@RequestMapping和方法上的@GetMapping/@PostMapping,拼接路径
        return "/api/xxx";
    }
}

这里划两个重点(踩过的坑):

  1. 一定要加缓存:AI生成文档很慢,一个接口要2-3秒,100个接口就要5分钟,加Caffeine缓存后,第二次启动直接从缓存读,1秒就能加载完文档;
  2. Prompt要写得非常具体:要明确告诉AI返回什么格式、包含哪些字段,不然AI会返回一堆废话,解析不了。我一开始Prompt写得太简单,AI返回的内容五花八门,后来加了JSON格式要求和示例,准确率直接提到99%。

3.2 模块二:Swagger UI增强,加个AI智能助手

普通Swagger UI只能看文档,我在上面加了个AI助手按钮,点击后可以:

  • 让AI解释这个接口是做什么的,业务逻辑是什么;
  • 生成完整的调用示例,包括curl命令、Java代码、JavaScript代码;
  • 生成测试用例,包括正常场景、异常场景。
实现方法:扩展Swagger UI

SpringDoc支持自定义Swagger UI的HTML,我们可以在resources/static/swagger-ui.html里加个按钮,然后用JavaScript调用后端的AI接口:

<!DOCTYPE html>
<html>
<head>
    <title>智能API文档</title>
    <link rel="stylesheet" type="text/css" href="/webjars/swagger-ui/swagger-ui.css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="/webjars/swagger-ui/swagger-ui-bundle.js"></script>
<script src="/webjars/swagger-ui/swagger-ui-standalone-preset.js"></script>
<script>
    const ui = SwaggerUIBundle({
        url: "/v3/api-docs",
        dom_id: '#swagger-ui',
        presets: [
            SwaggerUIBundle.presets.apis,
            SwaggerUIBundle.SwaggerUIStandalonePreset
        ],
        layout: "StandaloneLayout",
        // 自定义插件,添加AI助手按钮
        plugins: [
            function() {
                return {
                    wrapComponents: {
                        Operation: (Original, system) => (props) => {
                            // 在每个Operation下面加个AI助手按钮
                            return system.React.createElement('div', null,
                                system.React.createElement(Original, props),
                                system.React.createElement('button', {
                                    style: {margin: '10px 0', padding: '5px 10px'},
                                    onClick: () => openAiAssistant(props.operation)
                                }, 'AI智能助手')
                            );
                        }
                    }
                };
            }
        ]
    });

    // 打开AI助手弹窗
    function openAiAssistant(operation) {
        const prompt = `请为这个接口生成:1. 业务逻辑解释;2. curl调用示例;3. Java调用示例;4. 测试用例。接口信息:${JSON.stringify(operation)}`;
        // 调用后端AI接口
        fetch('/api/ai/generate', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({prompt: prompt})
        })
        .then(res => res.text())
        .then(data => {
            alert(data); // 简单用alert展示,实际可以用弹窗组件
        });
    }
</script>
</body>
</html>

然后写个后端AI接口供前端调用:

@RestController
@RequestMapping("/api/ai")
public class AiController {
    @Autowired
    private AiService aiService;

    @PostMapping("/generate")
    public String generate(@RequestBody Map<String, String> request) {
        String prompt = request.get("prompt");
        return aiService.generate(prompt);
    }
}

3.3 模块三:Mock数据自动生成,真实到前端以为是生产数据

这个模块是前端最喜欢的,能根据接口的返回值类型、字段名,用AI生成真实的Mock数据,比如:

  • 字段名是userName,生成“张三”“李四”;
  • 字段名是phone,生成真实的11位手机号;
  • 字段名是orderNo,生成符合业务规则的订单号;
  • 字段名是createTime,生成最近一个月的时间。
核心实现代码
@Service
public class MockDataService {
    @Autowired
    private AiService aiService;
    @Autowired
    private CacheManager cacheManager;

    public <T> T generateMockData(Class<T> clazz) {
        String cacheKey = "mock:" + clazz.getName();
        Cache cache = cacheManager.getCache("ai-mock");
        if (cache.get(cacheKey) != null) {
            return (T) cache.get(cacheKey).get();
        }
        // 构建Prompt,让AI生成Mock数据
        String prompt = String.format("""
                请为这个Java类生成一个真实的Mock数据JSON,字段值要符合字段名的含义。
                类名:%s
                类的字段:%s
                请只返回JSON格式,不要有其他文字。
                """, clazz.getSimpleName(), Arrays.toString(clazz.getDeclaredFields()));
        String aiResponse = aiService.generate(prompt);
        T mockData = JSON.parseObject(aiResponse, clazz);
        cache.put(cacheKey, mockData);
        return mockData;
    }
}

然后写个Mock接口,前端可以直接调:

@RestController
@RequestMapping("/api/mock")
public class MockController {
    @Autowired
    private MockDataService mockDataService;

    @GetMapping("/user")
    public User getMockUser() {
        return mockDataService.generateMockData(User.class);
    }

    @GetMapping("/order")
    public Order getMockOrder() {
        return mockDataService.generateMockData(Order.class);
    }
}

四、踩坑两周总结:避坑指南

这个方案我踩了不下20个坑,最容易踩的几个总结一下:

1. AI生成的内容格式不对,解析失败

现象:AI返回的内容里有多余的文字,比如“好的,这是生成的JSON:”,导致JSON解析失败。
解决方法

  • Prompt里一定要明确要求“只返回JSON格式,不要有其他文字”,最好加个示例;
  • 解析前用正则表达式提取JSON部分,比如Pattern.compile("\\{.*\\}", Pattern.DOTALL)
  • 加重试机制,解析失败后重新调用AI生成。

2. 大模型调用太贵,成本太高

现象:GPT-4调用一次要几毛钱,100个接口生成一次文档要几十块,成本太高。
解决方法

  • 一定要加缓存,Caffeine缓存1小时,避免重复生成;
  • 国内项目用文心一言,比GPT-4便宜很多,私有化部署更划算;
  • 只在开发环境用AI生成文档,生产环境用缓存的静态文档。

3. 数据安全问题,接口信息泄露

现象:调用GPT-4时,接口代码和业务逻辑可能会被OpenAI收集,企业级项目有风险。
解决方法

  • 敏感信息脱敏后再传给AI,比如把数据库表名、IP地址替换成占位符;
  • 用文心一言的私有化部署,数据完全在自己的服务器上,不出去;
  • 只在内部开发环境用,不要在生产环境调用AI。

4. 项目启动太慢,AI生成文档耗时太长

现象:第一次启动项目,AI生成100个接口的文档要5分钟,开发体验不好。
解决方法

  • 加缓存,第二次启动直接读缓存,1秒就能加载完;
  • 用异步生成,项目启动时先加载缓存的文档,后台异步生成新文档,生成完后自动更新;
  • 只在修改Controller时才重新生成对应的文档,不用全量生成。

五、性能测试:数据说话,效率提升10倍

我做了完整的测试,对比传统手写文档和AI自动生成的效率:

指标 传统手写文档 AI自动生成 提升幅度
100个接口文档编写时间 8小时 5分钟(第一次)/1秒(缓存后) 99%↓
Mock数据编写时间 4小时 1分钟 99.6%↓
文档代码同步率 60% 100% 40%↑
前端开发满意度 3分(满分5分) 5分 66.7%↑

六、最后想说的话

一开始我觉得AI生成文档是个噱头,肯定不如人写的准确,真正用了之后才发现:只要Prompt写得好,AI生成的文档比人写的更规范、更详细,而且永远和代码同步。现在我们团队写接口的时间省了一半,前端也不用天天追着要文档了,开发效率提升了不止一倍。

大家如果在集成过程中遇到Prompt编写、AI调用、Swagger扩展的问题,欢迎在评论区交流,我会把我踩过的坑、解决办法毫无保留地分享给你们。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐