告别 Python!Java 本地部署 Qwen 3.5 实战,Ollama + Spring Boot 保姆级教程

告别 Python!Java 本地部署 Qwen 3.5 实战,Ollama + Spring Boot 保姆级教程

文章目录

无意间发现了一个ZEEKLOG大神的人工智能教程,忍不住分享一下给大家。很通俗易懂,重点是还非常风趣幽默,像看小说一样。床送门放这了👉 http://blog.ZEEKLOG.net/jiangjunshow

前言

本文手把手教你在 Windows/Linux 上通过 Ollama 本地部署阿里 Qwen 3.5 大模型,并用 Spring Boot 3.x + Spring AI 1.1.0 搭建 Java 后端服务。无需 Python 环境,5 行代码实现 AI 对话,附完整 Maven 依赖、YAML 配置和可运行的 Controller 代码,显存 8GB 就能跑 9B 参数模型。


一、Java 程序员的"Python 霸凌":我们受够了

说实话,这年头做 Java 开发真的有点憋屈。每次想玩点 AI 大模型,网上一搜教程,清一色的 Python:pip install 这、conda env 那,好像不会 Python 就不配搞 AI 似的。

我就纳闷了,咱们 Java 生态圈养了这么多年的 Spring Boot,难道就只能在旁边看着 Python 吃香的喝辣的?

更离谱的是,很多公司为了跑个本地大模型,非要单独部署一套 Python 微服务,然后让 Java 后端通过 HTTP 调来调去。这就好比你在家里想吃碗面条,非要点外卖让骑手从隔壁厨房端过来——明明灶台上就有锅,干嘛不自己煮?

好消息是,2025 年开始,局面彻底变了。阿里发布的 Qwen 3.5 系列模型在中文理解能力上直接屠榜,而 Ollama 这个"模型外卖平台"让本地部署变得比安装微信还简单。再加上 Spring AI 1.1.0 的强势更新,Java 开发者终于可以挺直腰杆说:不用 Python,我们也能玩转大模型!


二、Ollama:大模型界的"应用商店"

打个比方,如果大模型是各种菜系的大厨,那 Ollama 就是高端人才市场。你不需要亲自去猎头(配置 Python 环境)、不用装修厨房(安装 CUDA)、不用给大厨发工资(购买 API Key),只需要敲一行命令,就能让 Qwen 3.5 这个"满汉全席级大厨"来你家(本地电脑)上班。

2.1 安装 Ollama:比装 QQ 还简单

不管你用 Windows、Mac 还是 Linux,Ollama 的安装都堪称无脑:

Windows 用户:
直接访问 ollama.com,下载 .exe 文件双击运行,一路下一步。安装完成后,右下角会出现一个羊驼图标,右键点击选择"Open Web UI"就能验证是否成功。

Linux 用户(Ubuntu/CentOS):
打开终端,复制粘贴这行命令,回车:

curl-fsSL https://ollama.com/install.sh |sh

装好后执行:

systemctl status ollama 

看到绿色的 active (running) 就说明大厨已经到岗。

验证安装:

ollama --version

如果显示类似 ollama version 0.6.0 的版本号,恭喜你,地基已经打好。

2.2 硬件要求:别被吓退,8G 显存也能玩

很多人一听"本地部署大模型"就想到四路 RTX 4090,其实 Qwen 3.5 这次发了很多个尺寸,从手机能跑的 0.8B 到服务器级的 397B 都有。

对于咱们 Java 开发者做测试或者中小型企业应用,9B 版本是甜点选择:

  • 显存需求:8GB 就能流畅跑(量化版)
  • 响应速度:RTX 3060 级别显卡,首 token 延迟不到 1 秒
  • 中文能力:写作、代码、推理都不输 GPT-4 初版

如果你只有核显或者老笔记本,也可以试试 4B 版本,虽然聪明程度差了点,但写个 CRUD 代码、解释个算法还是没问题。


三、召唤 Qwen 3.5:让阿里大厨进厨房

Ollama 装好后,下载模型就是一句话的事,比 Docker pull 镜像还快。

3.1 拉取模型:一键召唤

打开终端(Windows 就是 CMD 或 PowerShell),输入:

ollama pull qwen3.5:9b 

这时候 Ollama 会从仓库下载模型权重,大概 6GB 左右,网速快的话十分钟搞定。下载过程中你可以去泡杯咖啡,进度条走完后,你的电脑里就住着一个精通中文的 AI 大脑了。

如果你想试试更小更快的版本,可以换:

ollama pull qwen3.5:4b # 适合轻薄本 ollama pull qwen3.5:35b # 需要 24G 显存,性能怪兽

3.2 本地测试:先尝后买

下载完成后,先别急着写代码,在终端里直接对话试试:

ollama run qwen3.5:9b 

你会看到光标等待输入,这时候打个招呼:

你好,请用 Java 写个单例模式,要带线程安全的那种 

如果 Qwen 3.5 立刻给你吐出一段完美的双重检查锁代码,还附带注释解释 volatile 关键字的作用,那就说明一切正常。按 Ctrl+D 退出对话模式。

重点来了:Ollama 默认会开启一个 REST API 服务,地址是 http://localhost:11434,而且神奇的是,它兼容 OpenAI 的接口格式!这意味着什么?意味着你不需要学新的 API 规范,直接用 OpenAI 的客户端都能连上,简直是降维打击。


四、Spring Boot 3.x 集成:Java 程序员的春天

好了,大厨已经就位,现在该 Java 出场当服务员了。我们要用 Spring AI 这个"翻译官",让 Java 代码能和 Qwen 3.5 顺畅交流。

4.1 项目初始化:选对战车

打开 Spring Initializr(start.spring.io),选择:

  • Project: Maven
  • Spring Boot: 3.3.x(必须 3.x,因为 Spring AI 需要 Jakarta EE 命名空间)
  • Java: 17 或 21
  • Dependencies: Spring Web、Spring AI

如果你习惯命令行,也可以:

spring init --dependencies=ai,web my-qwen-app 

4.2 Maven 配置:引入弹药库

Spring AI 目前最新稳定版是 1.1.0-M3(2025 年 10 月发布),虽然带 M 字样,但亲测生产环境稳如老狗。在 pom.xml 里加入 BOM 管理:

 org.springframework.ai spring-ai-bom 1.1.0-M3 pom import org.springframework.ai spring-ai-ollama-spring-boot-starter org.springframework.ai spring-ai-openai-spring-boot-starter 

注意这里我们同时引入了 Ollama 和 OpenAI 的 starter,后者是为了用它的 POJO 类,毕竟接口格式兼容,不用白不用。

4.3 YAML 配置:打通任督二脉

application.yml 里写上这几行,Spring AI 就能自动识别你本地的 Ollama:

spring:ai:ollama:base-url: http://localhost:11434chat:model: qwen3.5:9b options:temperature:0.7num-predict:2048chat:client:enabled:true

这里 temperature 相当于大厨的"创意程度",0.7 是平衡点,想让它更严谨就调低,想更发散就调高。num-predict 是最大生成 token 数,防止话痨。


五、代码实战:5 行代码跑通对话

配置搞定,现在写 Java 代码,简单到离谱。

5.1 极简版:直接注入 ChatClient

@RestController@RequestMapping("/ai")publicclassChatController{privatefinalChatClient chatClient;publicChatController(ChatClient.Builder chatClientBuilder){this.chatClient = chatClientBuilder.build();}@GetMapping("/ask")publicStringask(@RequestParamString question){return chatClient.prompt().user(question).call().content();}}

就这几行,一个 REST API 就搭好了。启动项目,浏览器访问:

http://localhost:8080/ai/ask?question=用Java写个快速排序,并解释时间复杂度 

你会看到 Qwen 3.5 返回的 Markdown 格式代码和详细解释,延迟也就几百毫秒。

5.2 进阶版:带历史记忆的对话

上面那个版本每次对话都是失忆状态,如果你想做上下文连续聊天,得加个记忆功能:

@ServicepublicclassChatService{privatefinalChatClient chatClient;// 用 Map 模拟用户会话缓存,生产环境建议换 RedisprivatefinalMap> sessionStore =newConcurrentHashMap<>();publicChatService(ChatClient.Builder chatClientBuilder){this.chatClient = chatClientBuilder.build();}publicStringchat(String sessionId,String userInput){// 获取或创建历史记录List history = sessionStore.computeIfAbsent(sessionId, k ->newArrayList<>());// 添加用户消息 history.add(newUserMessage(userInput));// 调用模型String response = chatClient.prompt().messages(history).call().content();// 添加 AI 回复到历史 history.add(newAssistantMessage(response));// 防止上下文过长,保留最近 10 轮if(history.size()>20){ history = history.subList(history.size()-20, history.size()); sessionStore.put(sessionId, history);}return response;}}

对应的 Controller:

@RestController@RequestMapping("/chat")publicclassConversationController{@AutowiredprivateChatService chatService;@PostMapping("/talk")publicStringtalk(@RequestBodyChatRequest request){return chatService.chat(request.getSessionId(), request.getMessage());}}@DataclassChatRequest{privateString sessionId;privateString message;}

这样你就可以用 Postman 测试连续对话了,第一轮问"我叫张三",第二轮问"我刚才说了什么",它能准确回答出来。


六、性能调优:让 Qwen 3.5 跑得更欢

本地部署最大的焦虑就是"我的显卡够不够用"。别慌,Ollama 提供了丰富的参数让你做取舍。

6.1 量化模型:显存不够,精度来凑

如果你只有 6GB 显存,跑 9B 模型可能会爆。这时候可以换个量化版本:

ollama pull qwen3.5:9b-q4_K_M 

q4_K_M 表示 4-bit 量化,模型体积从 6GB 压缩到 4GB 左右,虽然智商会下降 5%,但速度提升明显,而且省出来的显存可以塞更长的上下文。

6.2 上下文长度:别让 AI 失忆

Qwen 3.5 官方支持 256K 上下文,但默认 Ollama 可能只开 2K。在 Java 代码里可以动态调整:

ChatResponse response = chatClient.prompt().user(longText).options(OllamaOptions.builder().withNumCtx(16384)// 开启 16K 上下文.withTemperature(0.8).build()).call().chatResponse();

注意上下文越长,显存占用越高,16K 大概需要多占 2-3GB 显存,自己权衡。

6.3 流式输出:告别转圈圈

大模型生成文字是个逐字蹦的过程,如果你等它全部写完再返回,用户会以为卡死了。改成流式输出(SSE)体验好很多:

@GetMapping(value ="/stream", produces =MediaType.TEXT_EVENT_STREAM_VALUE)publicFluxstreamAsk(@RequestParamString question){return chatClient.prompt().user(question).stream().content();}

前端配合 EventSource 就能实现打字机效果,跟 ChatGPT 网页版一样丝滑。


七、避坑指南:我踩过的雷你别踩

虽然整套方案已经很成熟,但总有几个小坑会让你 debugging 到半夜。

坑 1:Ollama 默认只能本地访问

如果你在服务器上部署,发现其他机器调不通 11434 端口,那是因为 Ollama 默认绑定 127.0.0.1。需要设置环境变量:

exportOLLAMA_HOST=0.0.0.0:11434 

Windows 用户在系统环境变量里加就行。

坑 2:中文乱码

如果返回的中文显示为问号,检查 application.yml 加上:

server:servlet:encoding:charset: UTF-8enabled:trueforce:true

坑 3:内存泄漏

长时间运行后如果 Java 进程内存暴涨,可能是没有正确关闭响应流。确保使用 try-with-resources 或者正确管理 ChatClient 的生命周期。

坑 4:模型下载卡住

ollama pull 时如果进度条不动,大概率是网络问题。可以配置镜像加速,或者在路由器上开启科学上网。


八、写在最后:Java 的 AI 时代真的来了

写到这里,其实挺感慨的。几年前我们还在争论"Java 适合做 AI 吗",现在 Spring AI + Ollama 的组合已经让这件事变得比写个 CRUD 还简单。

Qwen 3.5 在中文场景下的表现确实惊艳,写代码能比肩 GPT-4,写文案甚至更有"人味儿"。最关键的是,这一切都在你本地的机器上运行,数据不用上传到别人的服务器,深夜加班调代码也不用怕按 token 收费收到天价账单。

所以,别再让 Python 成为你的心理阴影了。打开 IDEA,新建一个 Spring Boot 项目,半小时后你就能拥有一个属于自己的 AI 助手。这感觉,就像是在自家后院搭了个五星级厨房,想吃什么,自己说了算。

Read more

Java 注解与反射实战:自定义注解从入门到精通

Java 注解与反射实战:自定义注解从入门到精通

前言:注解到底是什么?         你是否经常在 Java 代码中看到@Override、@Deprecated这样的标记?这些就是注解 —— 一种给代码 "贴标签" 的机制。注解本身不直接影响代码执行,但能通过工具(如编译器)或框架(如 Spring)赋予代码额外含义。         自定义注解则是让我们根据业务需求创建专属 "标签",结合反射机制能实现强大的动态逻辑(比如日志记录、权限校验、ORM 映射等)。本文将从基础到实战,带你掌握自定义注解的定义、元注解的作用,以及如何通过反射让注解 "生效"。 一、自定义注解基础:@interface 关键字         自定义注解使用 @interface 关键字定义,本质上是一种特殊的接口(编译后会生成继承 java.lang.annotation.Annotation 的接口)

By Ne0inhk
javaSE初阶————多线程进阶(2)

javaSE初阶————多线程进阶(2)

今天来继续带大家学习多线程进阶部分啦,今天是最后一期啦,下期带大家做一些多线程的题,我们就可以开始下一个环节啦; 1,JUC(java.util.concurrent)的常见类 1)Callable 接口 我们之前学过Runnable接口,它是一个任务,我们可以在创建线程的时候把任务丢给线程使用匿名内部类等方法来完成创建对象,现在我们有了一个新的方法来创建任务,并且执行这个任务,就是我们的Callable接口,Runnable的run方法是没有返回值的,但是Callable提供了返回值,支持泛型,我们就能获取到我们想要的参数, 我们来看看是怎么用的; Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { return null; } }; 我们使用匿名内部类的方法创建一个Callable对象,并且重写call方法,就相当与重写Runnable的run方法, 我们是不能把这个对象直接放到线程的构造方法中的,因为Th

By Ne0inhk

Exception in thread “main“ java.lang.NoSuchMethodError: ‘java.lang.String org.junit.platform.engine.

初始化的项目出现junit报错 Exception in thread "main" java.lang.NoSuchMethodError: 'java.lang.String org.junit.platform.engine.discovery.MethodSelector.getMethodParameterTypes()' at com.intellij.junit5.JUnit5TestRunnerUtil.loadMethodByReflection(JUnit5TestRunnerUtil.java:127) at com.intellij.junit5.JUnit5TestRunnerUtil.buildRequest(JUnit5TestRunnerUtil.java:102) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:43) at

By Ne0inhk
Java 部署:滚动更新(K8s RollingUpdate 策略)

Java 部署:滚动更新(K8s RollingUpdate 策略)

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Java部署这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * Java 部署:滚动更新(K8s RollingUpdate 策略) * 什么是滚动更新(Rolling Update)? * 为什么 Java 应用特别需要滚动更新? * Kubernetes 滚动更新的核心机制 * 默认值 * 参数详解 * 构建一个支持滚动更新的 Java 应用 * 1. 创建 Spring Boot 项目 * 2. 编写主类 * 3. 添加控制器 * 4. 配置 Actuator 健康端点 * 5. 构建 Docker 镜像 * 编写 Kubernetes

By Ne0inhk