简介:
项目地址:https://github.com/whltaoin/var-ai-agent
本文使用的AI大模型均为阿里云的灵积模型(Qwen-Plus)
一、AI大模型简介
- 定义:基于深度学习架构,经海量数据训练、参数规模达亿级/万亿级的AI模型,是从“专用智能”迈向“通用人工智能”的核心载体。
- 核心特征:
- 海量参数:存储知识、捕捉复杂规律的基础(如GPT-4超万亿参数)。
- 海量数据训练:依托多源数据形成“认知框架”。
- 通用能力涌现:随规模增长自发获得跨任务、逻辑推理等未训练能力。
- 国外内优秀的AI大模型介绍:
模型名称 |
研发机构 |
核心特点 |
典型应用场景 |
GPT-4.5 |
OpenAI(美国) |
综合能力全球领先,推理、编程表现突出,支持32K上下文 |
科研分析、跨领域决策 |
Claude 3.7 Sonnet |
Anthropic(美国) |
HumanEval编程得分91.2,10万token长文档解析,安全合规性强 |
法律合同、金融风控 |
Gemini 2.0 Ultra |
Google DeepMind(美国) |
原生多模态,百万级上下文窗口,工业设计优化 |
跨模态分析、实时翻译 |
DeepSeek R1 |
深度求索(中国) |
国内综合最优,推理速度提升3倍,中文长文本处理强 |
政务文档、金融研报 |
Qwen2.5-Max |
阿里云(中国) |
Chatbot Arena全球第7,数学与编程单项冠军 |
跨境电商、多语言客服 |
文心一言4.0 |
百度(中国) |
MMLU中文评测第1,情感识别92% |
营销内容、政务问答 |
LLaMA 3 |
Meta(美国) |
700亿参数全开源,生态插件丰富 |
学术研究、轻量化部署 |
豆包大模型 |
字节跳动(中国) |
稀疏MoE低成本,多模态交互强,方言语音合成 |
移动端助理、工业质检 |
KimiGPT 2.0 |
月之暗面(中国) |
支持7.5万字长文本,法律/科研分析高效 |
法律条文分析、学术综述 |
SenseChat 5.5 |
商汤科技(中国) |
中文NLG领先,文科能力突出 |
自然语言生成、创意文案 |
PaLM-3 |
Google(美国) |
常识推理与数学编码强,响应速度快 |
教育解题、金融量化 |
Falcon-200B |
阿联酋TII |
1800亿参数开源,数学推理对标GPT-4 |
中东多语言服务 |
Cohere Command-R |
Cohere(加拿大) |
企业级生成AI,数据隐私保护强 |
客户服务自动化 |
Gopher-2 |
DeepMind(英国) |
强化学习优化,蛋白质结构预测突破 |
生物医药研发 |
GLM-4 Plus |
智谱AI(中国) |
视频通话交互优化,知识问答均衡 |
人机交互、创意写作 |
MPT-50B |
MosaicML(美国) |
开源低成本,训练效率高 |
初创企业MVP开发 |
Gemini 2.0 Flash |
Google(美国) |
轻量级推理,响应速度提升40% |
实时搜索摘要 |
Claude 3.5 Haiku |
Anthropic(美国) |
端侧部署优化,适合移动设备 |
移动端安全对话 |
360zhinao2-o1 |
360集团(中国) |
中文场景多学科均衡性强 |
安防、企业知识管理 |
特点概览
- 国际领先:GPT-4.5、Claude 3.7、Gemini 2.0在推理、多模态、长文本处理方面表现突出,多为闭源或需申请API。
- 国内崛起:DeepSeek R1、Qwen2.5-Max、文心一言4.0在中文任务与性价比上具竞争力,部分开源,利于本地化部署。
- 开源生态:LLaMA 3、Falcon-200B、Qwen等开源模型生态活跃,便于二次开发与研究。
💡 选型建议
- 若侧重通用能力与国际生态:优先GPT-4.5、Claude 3.7、Gemini 2.0。
- 若重视中文场景与成本控制:优先DeepSeek R1、Qwen2.5-Max、文心一言4.0。
- 若需快速定制与研究:选择开源模型如LLaMA 3、Falcon-200B、Qwen。
二、AI大模型分类
(一)按技术架构
架构类型 |
核心原理 |
优势 |
典型代表 |
Transformer |
基于自注意力机制,捕捉长距离关联 |
并行效率高、擅长NLP |
GPT系列、BERT、LLaMA |
CNN衍生 |
提取局部特征聚合为全局特征 |
强空间特征捕捉能力 |
ViT、EfficientNet-L |
多架构融合 |
结合Transformer/CNN等优势 |
适配多模态任务 |
CLIP、Flux |
(二)按模态类型
- 单模态:仅处理一种数据,如文本(GPT-4o文本版)、图像(Stable Diffusion)、音频(Whisper-L)。
- 多模态:处理2种及以上数据,实现跨模态理解与生成,代表有GPT-4o、Gemini Pro、文心一言4.0。
(三)按能力侧重
- 理解型:专注数据解读分析,如BERT(文本理解)、ResNet-152(图像识别)。
- 生成型:基于线索生成新内容,如GPT系列(文本/多模态)、Stable Diffusion(图像)、Sora(视频)。
(四)按应用场景
- 通用型:跨领域适配,支持对话、翻译等通用任务,代表有GPT-4o、文心一言。
- 专用型:垂直领域定制,如医疗(Med-PaLM 2)、金融(招银大模型)、教育(星火教育大模型)。
三、开发者如何学习AI大模型
作为开发者,不需要深入了解大模型的实现原理,而应该专注于:
(一)如何选择适合的 AI 大模型?
考虑应用场景需求;评估成本与性能平衡;数据隐私要求
(二)如何有效利用 AI?
了解如何接入 AI 大模型?比如 AI 平台和工具
如何在程序中调用 AI 大模型?比如 API、SDK 和 AI 开发框架
掌握提示工程技巧 Prompt Engineering
掌握大模型开发工作流
(三)了解模型能力边界
知道模型能做什么和不能做什么
了解模型可能出现的问题(比如幻觉)及处理方法
四、开发者如何对比和选择AI大模型
(一)按照程序适用角度
- SpringAi官网个模型适用对比图:https://docs.langchain4j.dev/integrations/language-models/
langchain4j官网模型对比图:https://docs.spring.io/spring-ai/reference/api/chat/comparison.html
(二)按照实际需求角度
对比维度 |
核心评估指标 |
实用评估方法 |
选型决策建议 |
基础能力匹配 |
1. 语言理解:MMLU(多领域)、C-Eval(中文)得分2. 生成质量:逻辑一致性、事实准确性3. 专业能力:Human Eval(代码)、GSM8K(数学)表现 |
1. 参考权威评测报告(如 Papers With Code)2. 设计测试集:用业务场景样本实测(如客服话术生成、代码调试)3. 对比同任务输出:如相同需求下不同模型的响应质量 |
1. 中文场景优先选文心一言 4.0、GLM-4(C-Eval 得分领先)2. 代码开发优先选 GPT-4、Claude 4(Human Eval≥70%)3. 通用场景选 Gemini 2.5 Pro、Qwen3(综合能力均衡) |
技术规格适配 |
1. 架构与规模:Transformer/MoE 架构、参数量(7B-1T+)2. 上下文长度:4K-2M token3. 推理性能:延迟(ms)、吞吐量(token/s) |
1. 查模型官方文档:确认架构类型与上下文上限2. 压力测试:模拟并发场景测响应速度(用 Locust 工具)3. 硬件适配性:小参数量模型(如 Llama 3-7B)适配消费级 GPU |
1. 长文本处理(如论文分析)选 Kimi K2(1M token)、Claude 4(2M token)2. 边缘部署选 Qwen3-1.8B、Llama 3-8B(轻量化)3. 高并发场景选 MoE 架构模型(如豆包,推理效率高) |
功能特性契合 |
1. 模态支持:文本 / 图像 / 音频 / 视频2. 扩展能力:函数调用、工具集成3. 开源属性:可微调性、权重可获取性 |
1. 功能实测:调用 API 测试多模态转换(如图文生成)2. 工具兼容性:验证与 Hugging Face、LangChain 的集成度3. 微调可行性:查是否提供 LoRA 微调脚本 |
1. 多模态应用选 GPT-4o、文心一言 4.5(文本 + 图像 + 语音)2. 智能体开发选 Gemini 2.5 Pro、Kimi K2(函数调用强)3. 二次开发选开源模型(Llama 3、Qwen3) |
商业化与工程落地 |
1. 成本:token 单价、包月套餐价格2. 部署方式:API 调用 / 私有化部署 / 混合部署3. 服务保障:SLA 承诺、技术支持响应速度 |
1. 成本测算:按日调用量估算月成本(如 10 万 token / 天的 API 费用)2. 部署测试:用 Docker 部署开源模型测资源占用3. 合规核查:确认是否符合数据本地化要求 |
1. 低成本试错选 API 调用(如通义千问、讯飞星火)2. 数据敏感场景选私有化部署(如百度文心企业版、腾讯混元)3. 稳定服务选 SLA≥99.9% 的模型(如 OpenAI、阿里云) |
生态与可持续性 |
1. 社区支持:GitHub 星数、Issue 解决速度2. 工具链:是否适配 PyTorch/TensorFlow3. 迭代频率:模型更新周期、功能升级速度 |
1. 查开源社区:Hugging Face 模型下载量、GitHub 贡献者数量2. 文档完整性:评估开发教程、API 文档清晰度3. 历史版本:分析模型迭代的能力提升幅度 |
1. 新手优先选生态完善的模型(Llama 3、Qwen3,教程丰富)2. 长期项目选迭代稳定的模型(GPT 系列、文心一言)3. 技术攻坚选社区活跃模型(DeepSeek R1、GLM-4) |
五、项目后端初始化
(一)环境说明
环境说明:
JDK:21
Maven:3.9
Spring Boot:3.4.10
application.yml配置
spring: application: name: var-ai-agent profiles: active: local server: port: 33333 servlet: context-path: /api # api接口前缀 # springdoc-openapi项目配置 springdoc: swagger-ui: path: /swagger-ui.html tags-sorter: alpha operations-sorter: alpha api-docs: path: /v3/api-docs group-configs: - group: 'default' paths-to-match: '/**' packages-to-scan: cn.varin.varaiagent.controller #对应Controller接口包路径 # knife4j的增强配置,不需要增强可以不配 knife4j: enable: true setting: language: zh_cn
application-local.yml
spring: application: name: spring-ai-alibaba-qwq-chat-client-example ai: dashscope: api-key: "阿里云申请的key" chat: options: model: qwen-plus
(二)工具导入
1. HuTool
Hutool 是主流的 Java 工具类库,集合了丰富的工具类,涵盖字符串处理、日期操作、文件处理、加解密、反射、正则匹配等常见功能。它的轻量化和无侵入性让开发者能够专注于业务逻辑而不必编写重复的工具代码。
官网地址:https://doc.hutool.cn/pages/index/#%F0%9F%8D%8Amaven
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.38</version> </dependency>
2. Knife4j
Knife4j 是基于 Swagger 接口文档的增强工具,提供了更加友好的 API 文档界面和功能扩展,例如动态参数调试、分组文档等。它适合用于 Spring Boot 项目中,能够通过简单的配置自动生成接口文档,让开发者和前端快速了解和调试接口,提高写作效率。
参考官网:https://doc.xiaominfo.com/docs/quick-start#spring-boot-3
启动项目访问地址:http://ip:port/api/doc.html
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency>
访问效果:
3. lombok
Lombok 是一个 Java 库,它通过注解的方式减少 Java 代码中的模板代码,让开发者能够更简洁地编写代码。它的核心思想是通过注解处理器在编译阶段自动生成 getter、setter、构造函数等重复代码,从而提高开发效率并使代码更易维护。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
六、程序接入Ai大模型的四种方式
(一)SDK接入
SDK(软件开发工具包)是官方提供的最直接的集成方式,通常提供了完善的类型支持和错误处理机制
接入步骤:
<dependency> <groupId>com.alibaba</groupId> <artifactId>dashscope-sdk-java</artifactId> <!-- 请将 'the-latest-version' 替换为最新版本号:https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java --> <version>2.21.10</version> </dependency>
- 申请API Key:https://bailian.console.aliyun.com/
- 调用方式示例(本文选择:dashScope):https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api#ab9194e9a55dk
package cn.varin.varaiagent.invoke;// 建议dashscope SDK的版本 >= 2.12.0 import java.util.Arrays; import java.lang.System; import java.util.Map; import cn.varin.varaiagent.config.AliyunConfig; import com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.GenerationParam; import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.common.Message; import com.alibaba.dashscope.common.Role; import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.InputRequiredException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.utils.JsonUtils; import com.alibaba.fastjson.JSON; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.xml.validation.Validator; public class SDKInvoke { public GenerationResult callWithMessage() throws ApiException, NoApiKeyException, InputRequiredException { Generation gen = new Generation(); Message systemMsg = Message.builder() .role(Role.SYSTEM.getValue()) .content("You are a helpful assistant.") .build(); Message userMsg = Message.builder() .role(Role.USER.getValue()) .content("今天三明的天气怎么样?") .build(); GenerationParam param = GenerationParam.builder() // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx") .apiKey(AliyunConfig.accessKeyId) // 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models .model("qwen-plus") .messages(Arrays.asList(systemMsg, userMsg)) .resultFormat(GenerationParam.ResultFormat.MESSAGE) .build(); return gen.call(param); } public static void main(String[] args) { try { GenerationResult result = new SDKInvoke(). callWithMessage(); System.out.println(JsonUtils.toJson(result)); Map map= JSON.parseObject(JsonUtils.toJson(result),Map.class); System.out.println(map.get("output")); } catch (ApiException | NoApiKeyException | InputRequiredException e) { // 使用日志框架记录异常信息 System.err.println("An error occurred while calling the generation service: " + e.getMessage()); } System.exit(0); } }
效果:
{ "requestId": "64993a53-3d15-4d14-a0a9-5b1a4f8c1980", "usage": { "input_tokens": 23, "output_tokens": 36, "total_tokens": 59, "prompt_tokens_details": { "cached_tokens": 0 } }, "output": { "choices": [ { "finish_reason": "stop", "message": { "role": "assistant", "content": "I'm functioning well, thank you for asking! I appreciate your concern. I'm here and ready to help with whatever you need. How can I assist you today? 😊" } } ] } }
(三)HTTP接入
curl -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ -H "Authorization: Bearer $DASHSCOPE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen-plus", "messages": [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": "你是谁?" } ] }'
- 因为是AI时代,hutool工具中又提供了网络请求的方法,所以说将Curl传给任意的AI工具生成对应代码
package cn.varin.varaiagent.invoke; //http接入大模型 import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.varin.varaiagent.config.AliyunConfig; public class HttpInvoke { public static void main(String[] args) { // 替换为你的API密钥 String apiKey = AliyunConfig.accessKeyId; if (apiKey == null || apiKey.isEmpty()) { System.err.println("请设置环境变量DASHSCOPE_API_KEY"); return; } // 构建请求URL String url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"; // 构建请求体JSON JSONObject requestBody = new JSONObject(); requestBody.put("model", "qwen-plus"); // 构建messages数组 JSONArray messages = new JSONArray(); // 添加system消息 JSONObject systemMsg = new JSONObject(); systemMsg.put("role", "system"); systemMsg.put("content", "You are a helpful assistant."); messages.add(systemMsg); // 添加user消息 JSONObject userMsg = new JSONObject(); userMsg.put("role", "user"); userMsg.put("content", "给我介绍一本关于学习Java的书籍"); messages.add(userMsg); requestBody.put("messages", messages); // 发送POST请求 HttpResponse response = HttpRequest.post(url) .header("Authorization", "Bearer " + apiKey) .header("Content-Type", "application/json") .body(requestBody.toString()) .execute(); // 输出响应结果 System.out.println("响应状态码: " + response.getStatus()); System.out.println("响应内容: " + response.body()); } }
结果:
响应内容: { "choices": [ { "message": { "role": "assistant", "content": "当然可以!一本广受好评且非常适合学习Java的书籍是:\n\n**《Java核心技术 卷I:基础知识》(原书第11版)** \n作者:[美] 凯·S. 霍斯特曼(Cay S. Horstmann)\n\n这本书被广泛认为是学习Java的经典教材之一,特别适合初学者和有一定编程基础的学习者。\n\n### 主要特点:\n- **内容全面系统**:涵盖了Java语言的核心概念,包括数据类型、控制结构、面向对象编程、异常处理、泛型、集合框架、Lambda表达式和Stream API等。\n- **结合实际案例**:书中提供了大量清晰、实用的代码示例,帮助读者理解理论知识在实际开发中的应用。\n- **紧跟Java新特性**:第11版更新了对Java 8到Java 11中新特性的讲解,如函数式编程、模块化系统(Jigsaw)简介等。\n- **语言通俗易懂**:虽然内容深入,但作者讲解清晰,循序渐进,适合自学。\n- **权威推荐**:被众多高校和培训机构用作教材,也被Java开发者社区高度认可。\n\n### 适合人群:\n- Java初学者\n- 希望系统掌握Java核心知识的程序员\n- 准备转行进入Java开发领域的学习者\n\n### 补充建议:\n如果你已经掌握基础,想进一步深入,可以继续阅读《Java核心技术 卷II:高级特性》。\n\n此外,配合这本书学习时,建议动手编写代码,结合IDE(如IntelliJ IDEA或Eclipse)进行实践,效果更佳。\n\n如果你想看中文资源,这本书有高质量的中文翻译版,由机械工业出版社出版,书名是《Java核心技术 卷I:基础知识》。\n\n希望这本书能帮助你扎实掌握Java编程!如果你有特定学习目标(如准备面试、做Web开发等),我也可以推荐更有针对性的书籍。" }, "finish_reason": "stop", "index": 0, "logprobs": null } ], "object": "chat.completion", "usage": { "prompt_tokens": 27, "completion_tokens": 407, "total_tokens": 434, "prompt_tokens_details": { "cached_tokens": 0 } }, "created": 1758975181, "system_fingerprint": null, "model": "qwen-plus", "id": "chatcmpl-6fa00c34-ee26-4d20-b2f4-0a6d82a90277" }
(三)SpringAI接入
- 本文使用的是阿里封装过的SpringAi:Spring AI Alibaba
- 官网地址:https://java2ai.com/
- 使用教程:https://java2ai.com/docs/1.0.0-M6.1/overview/?spm=5176.29160081.0.0.2856aa5cdpZuC1
- 本文使用的版本为:1.0.0-M6.1
- 导入依赖:
<dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> <version>1.0.0-M6.1</version> </dependency>
- 实现
CommandLineRunner类中的run方法可以在springboot启动时,直接执行。
可以间接的替代了测试方法
package cn.varin.varaiagent.invoke; import jakarta.annotation.Resource; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class SpringAiInvoke implements CommandLineRunner { @Resource private ChatModel dashScopeChatModel; @Override public void run(String... args) throws Exception { AssistantMessage hello = dashScopeChatModel.call(new Prompt("解释AI一词")) .getResult() .getOutput(); System.out.println(hello.getText()); } }
- 效果:
(四):langchain4j接入
- 简介:
LangChain4j 是一个专注于构建基于大语言模型(LLM)应用的 Java 框架,作为知名 AI 框架 LangChain 的 Java 版本,它提供了丰富的工具和抽象层,简化了与 LLM 的交互和应用开发。
接入灵积模型示例:https://docs.langchain4j.dev/integrations/language-models/dashscope/
- 导入依赖
<!-- https://mvnrepository.com/artifact/dev.langchain4j/langchain4j-community-dashscope --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-community-dashscope</artifactId> <version>1.5.0-beta11</version> </dependency>
- 实现
package cn.varin.varaiagent.invoke; import cn.varin.varaiagent.config.AliyunConfig; import dev.langchain4j.community.model.dashscope.QwenChatModel; import dev.langchain4j.model.chat.ChatModel; public class Langchaiin4jInvoke { public static void main(String[] args) { // AliyunConfig.accessKeyId为在阿里云申请的key ChatModel build = QwenChatModel.builder().apiKey(AliyunConfig.accessKeyId) .modelName("qwen-plus") .build(); String helloWorld = build.chat("hello world"); System.out.println(helloWorld); } }
- 效果
(五)四种接入方式优缺点对比
接入方式 |
优点 |
缺点 |
适用场景 |
SDK调用 |
开发便捷性高,只需引入依赖即可像调用本地方法一样调用AI能力;稳定性与可靠性强,官方维护,对网络波动、重试机制等进行了优化;类型安全,有清晰的请求和响应对象;生态支持好,通常有完整文档、示例代码和社区支持。 |
项目需要引入额外的SDK依赖;需关注SDK版本更新,可能存在兼容性问题;对最新AI模型或功能的适配可能存在滞后。 |
大多数业务应用场景,如需要稳定、高效、便捷接入AI能力的场景。 |
HTTP调用 |
语言无关性,任何支持HTTP请求的编程语言都可调用,灵活性极高;无需额外依赖,使用语言自带的HTTP客户端库即可;能更快获得最新功能,AI服务提供商通常先发布HTTP API。 |
需要手动构建请求URL、设置请求头和请求体,解析响应体,开发成本高;网络延迟可能影响用户体验;数据隐私风险相对较高;定制化程度相对较低;频繁调用成本可能较高。 |
对灵活性要求极高,需要与AI服务进行极低级别交互,或官方SDK不支持最新功能的场景。 |
Spring AI |
与Spring Boot、Spring Cloud等生态组件无缝衔接,依赖管理方便;提供标准化的API抽象层,降低AI开发技术门槛;具备事务管理、安全认证、监控与追踪等企业级特性。 |
主要支持OpenAI等海外模型,对国内模型支持相对较弱;更适合简单问答、provider封装、API集成等场景,对于复杂的多步骤推理等场景支持不如LangChain4j。 |
已采用Spring技术栈的企业进行业务系统智能化改造,如银行核心系统风控模型集成;微服务架构下的AI服务部署;医疗、金融等对数据安全和合规性要求严格的行业应用。 |
LangChain4j |
对多种大语言模型兼容,可在运行时动态切换模型;提供丰富的提示构建工具和强大的上下文管理能力;支持与外部工具深度集成,通过FunctionCall机制实现模型与工具协同工作。 |
学习成本略高,偏工程化;不是原生集成Spring生态,需要进行集成操作。 |
构建复杂对话系统、知识问答系统、自动化工具机器人等;创新型AI产品快速原型开发;学术研究与定制化模型探索;多语言、多模态交互应用。 |
七、扩展知识:本地搭建AI大模型及调用
- 具体安装参照往期文章:https://blog.csdn.net/weixin_45793745/article/details/146604715?spm=1001.2014.3001.5501
- 具体调用参照往期文章:https://blog.csdn.net/weixin_45793745/article/details/147540722?spm=1001.2014.3001.5501
加油呀~