首页
关于这个博客
Search
1
Java 实现Google 账号单点登录(OAuth 2.0)全流程解析
1,871 阅读
2
Spring AI 无法获取大模型深度思考内容?解决方案来了
653 阅读
3
大模型“无脑复读”?3分钟搞懂原因+解决办法
503 阅读
4
EasyExcel 实战:导出带图片的 Excel 完整方案
355 阅读
5
服务器遭遇 XMRig 挖矿程序入侵排查与清理全记录
329 阅读
Java 核心
框架与中间件
数据库技术
开发工具与效率
问题排查与踩坑记录
程序员成长与思考
前端
AI大模型&智能体
登录
Search
标签搜索
Spring AI
java虚拟机
JVM
Spring AI Alibaba
Java
保姆级教程
SpringBoot
Spring
MCP
WebFlux
龙虾
Agent Skills
大模型
OpenClaw
人工介入
Nginx
Ubuntu
Apache POI
自定义starter
Mybatis
Luca Ju
累计撰写
54
篇文章
累计收到
2
条评论
首页
栏目
Java 核心
框架与中间件
数据库技术
开发工具与效率
问题排查与踩坑记录
程序员成长与思考
前端
AI大模型&智能体
页面
关于这个博客
搜索到
21
篇与
的结果
2025-10-16
Spring Boot 整合 Milvus 向量数据库:CRUD封装
本文围绕 Spring Boot 集成 Milvus 向量数据库展开,详细介绍了二次封装的完整方案:首先通过配置类实现 Milvus 连接池与连接参数管理,再以@CollectionName和@PrimaryKey注解建立实体与 Milvus Collection 的映射,搭配驼峰转下划线命名策略解决字段名适配问题;核心封装泛型MilvusBaseService
类,实现新增、主键查询、分页查询等通用 CRUD 方法,并针对 Milvus 无原生更新的特性,采用 “先删除后插入” 策略实现更新功能;同时提供 Lambda 查询构造器简化条件查询,辅以工具类处理主键解析等通用逻辑。文中还给出业务层 Service、Controller 的使用示例与完整配置文件,附项目 GitHub 地址,帮助开发者快速落地 Milvus 相关业务。
2025年10月16日
169 阅读
0 评论
4 点赞
2025-09-30
Java 实战:基于 WebSocket 获取大模型流式输出并转为 Flux
本文详细讲解 Java 如何基于 ReactorNettyWebSocketClient 实现 WebSocket 流式输出获取,附带完整代码演示将 WebSocket 消息转为 Flux 响应式流的全流程,包含连接建立、异常处理、会话管理等关键逻辑,助力开发者快速解决大模型流式对接需求,适配前端 HTTP 流式交互场景。
2025年09月30日
131 阅读
0 评论
2 点赞
2025-09-19
Java 实现Google 账号单点登录(OAuth 2.0)全流程解析
在当今的互联网应用中,单点登录(SSO)已成为提升用户体验的重要功能 —— 用户无需重复注册账号,通过常用的第三方平台(如 Google、GitHub)即可快速登录。本文将详细介绍如何基于 OAuth 2.0 协议,使用 Java 结合 JustAuth 框架实现 Google 账号单点登录,涵盖从 Google 平台配置到前后端代码落地的完整流程。一、前置知识:Google 账号登录核心原理Google 账号登录基于 OAuth 2.0 授权协议,核心流程可概括为 3 步:应用引导用户跳转至 Google 授权页面,用户完成身份验证并同意授权;Google 授权服务器向应用的回调地址返回授权码;应用通过授权码向 Google 申请访问令牌,进而获取用户基本信息,最终完成登录逻辑。本文将使用 JustAuth 框架(轻量级 OAuth 工具包)简化开发,避免重复编写 OAuth 协议相关的底层代码。二、前期准备:Google Cloud 平台服务配置要实现 Google 登录,首先需要在 Google Cloud 平台创建应用并配置授权信息,这是整个流程的基础。1. 登录 Google Cloud 平台访问 Google Cloud 控制台,使用 Google 账号登录。2. 创建 / 选择项目若已有项目:直接在顶部项目列表中选择目标项目;若新建项目:点击顶部「项目」→「新建项目」,输入项目名称(如「Java-Google-SSO-Demo」),完成创建后等待项目初始化(约 1-2 分钟)。3. 配置「OAuth 同意屏幕」新创建的项目必须先配置「同意屏幕」,否则无法创建客户端凭证。在左侧菜单找到「API 和服务」→「OAuth 同意屏幕」;选择「外部」(适用于面向公众的应用,内部应用仅适用于企业域内用户),点击「创建」;填写「应用信息」:应用名称:将显示在 Google 登录授权页面(如「我的 Java 应用」);支持邮箱:用于接收 Google 相关通知;后续步骤(范围、测试用户、摘要)保持默认配置,点击「提交」即可(测试阶段无需审核,直接可用)。4. 创建 Client 客户端(关键!)客户端凭证(Client ID、Client Secret)是应用与 Google 授权服务器通信的身份标识,需重点配置。在左侧菜单找到「API 和服务」→「凭据」→「创建凭据」→「OAuth 客户端 ID」;应用类型选择「Web 应用」,填写名称(如「Java-Web-SSO」);配置回调地址(Redirect URI):这是 Google 授权成功后跳转的地址,必须与后端代码中配置的一致(如 http://localhost:8080/google-login-callback);本地测试可添加 http://localhost:8080/*,线上环境需填写真实域名;点击「创建」,记录生成的 Client ID 和 Client Secret(后续代码会用到,切勿泄露)。三、前端实现:Google 登录按钮与跳转前端只需提供一个登录按钮,点击后跳转至后端接口,由后端引导至 Google 授权页面(避免前端直接与 Google 交互,保证安全性)。本文以 Vue 为例,其他框架(React、Angular)逻辑一致:<template> <!-- 页面中的 Google 登录按钮 --> <button class="google-login-btn" @click="handleGoogleLogin" > <i class="icon-google"></i> 用 Google 账号登录 </button> </template> <script> export default { name: 'LoginPage', methods: { /** * 处理 Google 登录点击事件 * 核心:跳转至后端接口,由后端重定向到 Google 授权页 */ handleGoogleLogin() { // 后端接口地址(需与后端服务端口一致,如本地 8080 端口) window.location.href = "http://localhost:8080/google-login"; } } }; </script> <style scoped> .google-login-btn { padding: 8px 16px; background: #4285F4; color: white; border: none; border-radius: 4px; cursor: pointer; } </style>注意:此处需使用 window.location.href 进行页面级跳转,而非异步请求(axios/fetch)—— 因为 Google 授权页面需要用户手动交互(输入账号密码、同意授权)。四、后端实现:基于 JustAuth 框架的核心逻辑后端使用 Spring Boot + JustAuth 框架,主要实现两个接口:google-login:引导用户跳转至 Google 授权页;google-login-callback:接收 Google 回调,处理授权逻辑并完成登录。1. 第一步:添加 JustAuth 依赖在 pom.xml 中引入 JustAuth 最新版本(版本可从 JustAuth GitHub 发布页 获取):<!-- JustAuth:轻量级 OAuth 工具包,简化第三方登录开发 --> <dependency> <groupId>me.zhyd.oauth</groupId> <artifactId>JustAuth</artifactId> <version>1.16.5</version> <!-- 替换为最新版本 --> </dependency> <!-- 若使用 Spring Boot,需确保已引入 web 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>2. 第二步:实现「引导授权」接口(/google-login)该接口的作用是:接收前端登录请求,生成 Google 授权地址,并将页面重定向至该地址。import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.request.AuthGoogleRequest; import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.utils.AuthStateUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; @RestController @AllArgsConstructor public class AuthController { /** * Google 登录引导:重定向至 Google 授权页 * @param response 用于重定向的响应对象 * @throws IOException 重定向异常 */ @GetMapping("/google-login") public void googleLogin(HttpServletResponse response) throws IOException { // 1. 创建 Google 授权请求对象 AuthRequest authRequest = getAuthRequest(); // 2. 生成授权地址(包含随机 State,防止 CSRF 攻击) String googleAuthUrl = authRequest.authorize(AuthStateUtils.createState()); // 3. 重定向至 Google 授权页 response.sendRedirect(googleAuthUrl); } /** * 封装 Google 授权配置(抽离为工具方法,便于复用) * @return AuthRequest 授权请求对象 */ private AuthRequest getAuthRequest() { return new AuthGoogleRequest(AuthConfig.builder() // 1. 填入 Google Cloud 配置的 Client ID .clientId("你的 Client ID") // 2. 填入 Google Cloud 配置的 Client Secret .clientSecret("你的 Client Secret") // 3. 填入 Google Cloud 配置的回调地址(必须一致!) .redirectUri("http://localhost:8080/google-login-callback") // 4. (可选)国内环境配置代理(Google 服务需科学上网) .httpConfig(HttpConfig.builder() .timeout(15000) // 超时时间:15秒 .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7897))) // 本地代理地址 .build()) .build()); } }关键说明:AuthStateUtils.createState():生成随机 State 参数,用于防止跨站请求伪造(CSRF)攻击,Google 会在回调时带回该参数,JustAuth 会自动校验;代理配置:国内环境访问 Google 服务需配置代理(如 Clash、V2Ray),需将 host 和 port 替换为本地代理的实际参数;Client ID/Secret/Redirect URI:必须与 Google Cloud 配置完全一致,否则会报「无效客户端」或「回调地址不匹配」错误。3. 第三步:实现「回调处理」接口(/google-login-callback)用户在 Google 授权页完成操作后,Google 会将页面重定向至我们配置的回调地址,并携带 授权码(code)和 State 参数。该接口需完成以下逻辑:接收 Google 回调参数;通过 JustAuth 解析授权码,获取用户信息;结合业务逻辑完成登录(如创建用户、生成自定义 Token);重定向至前端首页(携带自定义 Token)。import me.zhyd.oauth.model.AuthResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @RestController public class AuthController { // 从配置文件读取前端首页地址(如:http://localhost:8081/home?token=) @Value("${login.redirect.page}") private String loginRedirectPage; // 注入业务层服务(处理用户登录逻辑) private final AuthService authService; public AuthController(AuthService authService) { this.authService = authService; } /** * Google 登录回调:处理授权逻辑,完成登录 * @param callback Google 回调参数(包含 code、state 等) * @param response 用于重定向至前端首页 * @throws IOException 重定向异常 */ @GetMapping("/google-login-callback") public void googleLoginCallback(AuthCallback callback, HttpServletResponse response) throws IOException { // 1. 创建授权请求对象(复用之前的配置) AuthRequest authRequest = getAuthRequest(); // 2. 解析回调参数,获取 Google 用户信息(JustAuth 自动处理 code 换 token、拉取用户信息) AuthResponse authResponse = authRequest.login(callback); // 3. 业务层处理:结合 Google 用户信息完成登录(核心逻辑) // 自定义 VO:包含系统生成的 Token、用户信息等 AuthLoginRespVO loginResult = authService.doGoogleLogin(authResponse); // 4. 重定向至前端首页,并携带系统 Token(前端通过 Token 验证登录状态) response.sendRedirect(loginRedirectPage + loginResult.getAccessToken()); } // (省略 getAuthRequest() 方法,与上文一致) }4. 补充:业务层登录逻辑(AuthService)doGoogleLogin 方法是业务核心,需根据项目需求实现(如「用户首次登录自动创建账号」「已存在用户直接关联登录」)。以下是一个简化示例:import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthUser; import org.springframework.stereotype.Service; @Service public class AuthService { /** * Google 登录业务逻辑 * @param authResponse JustAuth 返回的 Google 用户信息 * @return AuthLoginRespVO 系统登录结果(含 Token) */ public AuthLoginRespVO doGoogleLogin(AuthResponse authResponse) { // 1. 解析 Google 用户信息(AuthUser 包含用户名、邮箱、头像等) AuthUser googleUser = (AuthUser) authResponse.getData(); String googleOpenId = googleUser.getUuid(); // Google 用户唯一标识(OpenID) String googleEmail = googleUser.getEmail(); // Google 邮箱(需用户授权才能获取) // 2. 数据库查询:判断用户是否已关联 Google 账号 User existingUser = userMapper.selectByGoogleOpenId(googleOpenId); User loginUser; if (existingUser != null) { // 3. 已关联用户:直接更新登录时间等信息 existingUser.setLastLoginTime(new Date()); userMapper.updateById(existingUser); loginUser = existingUser; } else { // 4. 未关联用户:创建新用户(或引导绑定已有账号) loginUser = new User(); loginUser.setGoogleOpenId(googleOpenId); loginUser.setUsername(googleUser.getNickname()); loginUser.setEmail(googleEmail); loginUser.setAvatar(googleUser.getAvatar()); loginUser.setCreateTime(new Date()); userMapper.insert(loginUser); } // 5. 生成系统自定义 Token(如 JWT Token) String systemToken = jwtUtils.generateToken(loginUser.getId()); // 6. 返回登录结果(Token + 用户基本信息) return new AuthLoginRespVO() .setAccessToken(systemToken) .setUsername(loginUser.getUsername()) .setAvatar(loginUser.getAvatar()); } }五、整体流程回顾用户点击前端「Google 登录」按钮,跳转至后端 /google-login 接口;后端生成 Google 授权地址,重定向至 Google 授权页;用户在 Google 页输入账号密码,同意授权后,Google 重定向至后端 /google-login-callback 接口,并携带 code;后端通过 code 向 Google 申请 Token,拉取用户信息;后端结合用户信息完成登录逻辑,生成系统 Token;后端重定向至前端首页,携带 Token;前端解析 Token,验证登录状态,跳转至首页。六、常见问题与注意事项回调地址不匹配:检查 Google Cloud 配置的 Redirect URI 与后端 redirectUri 是否完全一致(包括协议、域名、端口);国内环境无法访问 Google:需配置代理(如上文的 httpConfig),确保后端服务能正常调用 Google 授权接口;用户信息无法获取:需在 Google Cloud「OAuth 同意屏幕」中添加「邮箱」「个人资料」等权限范围;State 验证失败:JustAuth 会自动校验 State,若报错需检查是否在授权过程中修改了 State 参数;Client Secret 泄露风险:Client Secret 需存储在后端配置文件(如 application.yml),切勿暴露在前端代码中。七、总结本文基于 OAuth 2.0 协议和 JustAuth 框架,实现了 Google 账号单点登录的完整流程,核心在于「Google 平台配置」「授权引导」「回调处理」三个环节。JustAuth 框架极大简化了 OAuth 协议的底层实现,让开发者可以专注于业务逻辑(如用户关联、Token 生成)。实际项目中,还可根据需求扩展功能,如「账号绑定」「登录日志记录」「Token 过期刷新」等。希望本文能为你的第三方登录开发提供参考!
2025年09月19日
1,871 阅读
0 评论
4 点赞
2025-08-18
从零开始:Java 服务部署与 HTTPS 访问全攻略
前言在现代 Web 应用开发中,将 Java 服务部署到服务器并通过 HTTPS 安全访问是一项基础且重要的技能。本文将详细记录从环境准备到最终实现 HTTPS 访问的完整过程,适合新手开发者参考学习。一、JDK 环境安装Java 服务运行依赖 JDK 环境,我们选择安装开源免费的 OpenJDK 17 版本。1. 更新系统索引包首先需要更新 Ubuntu 系统的软件包索引,确保能获取到最新的软件版本信息:sudo apt update2. 安装 OpenJDK 17执行以下命令安装 OpenJDK 17:sudo apt install openjdk-17-jdk安装过程中系统会自动处理依赖关系,无需额外操作。3. 验证 JDK 安装结果安装完成后,通过以下命令验证是否安装成功:java -version如果安装成功,会显示类似如下的版本信息:openjdk version "17.0.x" 20xx-xx-xx OpenJDK Runtime Environment (build 17.0.x+xx-Ubuntu-1ubuntux) OpenJDK 64-Bit Server VM (build 17.0.x+xx-Ubuntu-1ubuntux, mixed mode, sharing)4. 配置 JAVA_HOME 环境变量为了让系统和其他工具能正确识别 JDK 位置,需要配置 JAVA_HOME 环境变量:echo "export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> ~/.bashrc source ~/.bashrc执行完成后,JAVA_HOME 环境变量将在每次登录时自动生效。二、Java 服务打包与部署完成 JDK 环境配置后,接下来需要将开发好的 Java 服务打包并上传到服务器。1. 服务打包使用 Maven 工具对 Java 项目进行打包,推荐通过 IDEA 的 Maven 插件操作:在 IDEA 右侧的 Maven 面板中找到项目展开 Lifecycle 选项双击 package 命令执行打包操作打包成功后,会在项目的 target 目录下生成对应的 JAR 文件2. 上传服务到服务器将打包好的 JAR 文件通过 SFTP 工具(如 Termius)或 scp 命令上传到服务器的指定目录,例如/opt/java/services/目录。3. 启动 Java 服务通过以下命令启动 Java 服务:nohup java -jar /opt/java/services/your-service.jar &其中&符号表示让服务在后台运行。如果需要更完善的服务管理,建议配置 systemd 服务。三、Nginx 配置实现 HTTPS 访问为了实现 HTTPS 访问并优化请求转发,我们需要配置 Nginx 作为反向代理服务器。1. 创建或编辑 Nginx 配置文件执行以下命令创建新的 Nginx 配置文件:sudo nano /etc/nginx/sites-available/your_domain.conf如果你已有配置文件,直接编辑对应文件即可。2. 配置 HTTPS 与反向代理在配置文件中添加以下内容,实现 HTTP 到 HTTPS 的跳转以及请求转发:server { listen 80; server_name yourdomain.com; # 替换为你的实际域名 # 将所有HTTP请求强制跳转至HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl; server_name yourdomain.com; # 替换为你的实际域名 # SSL证书配置(请替换为你的证书实际路径) ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # SSL安全优化配置 ssl_protocols TLSv1.2 TLSv1.3; # 支持的TLS协议版本 ssl_prefer_server_ciphers on; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_session_cache shared:SSL:10m; # SSL会话缓存设置 ssl_session_timeout 10m; # SSL会话超时时间 # 将所有 /app 开头的请求转发到Java服务的9999端口 location /app/ { proxy_pass http://127.0.0.1:9999; # 传递原始请求信息(HTTPS环境关键配置) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 重要:传递HTTPS协议信息 # 超时设置(根据实际服务需求调整) proxy_connect_timeout 300s; proxy_read_timeout 300s; } # 其他路径处理 location / { # 可以根据需求配置为返回404或指向其他服务 return 404; } }3. 启用 Nginx 配置创建符号链接将配置文件添加到 sites-enabled 目录,使 Nginx 能够识别该配置:sudo ln -s /etc/nginx/sites-available/your_domain.conf /etc/nginx/sites-enabled/4. 检查并重启 Nginx配置完成后,先检查配置文件是否有语法错误:sudo nginx -t如果显示 "nginx: configuration file /etc/nginx/nginx.conf test is successful",说明配置无误,可以重启 Nginx 使配置生效:sudo systemctl restart nginx四、验证 HTTPS 访问完成以上所有步骤后,通过浏览器访问https://yourdomain.com/app/user/login(替换为你的实际域名和接口路径),如果能正常访问并获得服务响应,说明整个部署过程成功完成。常见问题排查如果无法访问 HTTPS 服务,先检查服务器防火墙是否开放 443 端口证书路径错误会导致 Nginx 启动失败,需确保证书文件存在且权限正确Java 服务未启动或端口被占用会导致 502 错误,可通过netstat -tlnp查看端口占用情况配置文件修改后未重启 Nginx 会导致配置不生效,记得每次修改后重启服务通过本文的步骤,你已经成功实现了 Java 服务的部署和 HTTPS 访问配置。在实际生产环境中,还可以根据需求添加服务监控、日志管理等功能,进一步提升服务的稳定性和可维护性。
2025年08月18日
39 阅读
0 评论
0 点赞
2025-08-11
接入 DeepSeek | Java 调用大模型实战保姆级教程:新手快速上手
接入 DeepSeek | Java 调用大模型实战保姆级教程:新手快速上手最近各大平台接入 DeepSeek 大模型的案例越来越多,作为程序员,你是不是也想亲手实现一次?别担心,这篇教程会用3 种实战方式,带你从零开始用 Java 接入 DeepSeek 大模型,从 SDK 调用到原生 HTTP 请求全覆盖,新手也能轻松上手~前置知识:为什么需要响应式编程?大模型对话为了提升用户体验,通常会采用流式输出(像 ChatGPT 那样逐字显示回复)。而 Java 中实现流式输出需要用到响应式编程(Reactive Programming),核心是通过Flux(Reactor 框架)处理异步数据流。如果你对响应式编程不太熟悉,可以先简单理解:Flux能帮你 “实时接收” 大模型的每一段输出,而不是等全部结果返回后才处理。👉 快速入门参考:Spring 响应式编程官方文档👉 也可参考我的博客:响应式编程学习笔记准备工作:先获取 DeepSeek API Key在开始编码前,需要先申请 DeepSeek 的 API Key:访问 DeepSeek 开放平台:https://platform.deepseek.com/api_keys注册 / 登录账号后,在 “API Keys” 页面点击 “创建 API Key”,保存生成的密钥(后续代码中会用到)。方式一:通过 DeepSeek 官方 SDK 调用(最直接)DeepSeek 官方提供了适配 Spring AI 的 SDK,开箱即用,适合快速集成。1. 添加依赖在pom.xml中引入 Spring AI 对 DeepSeek 的支持(建议使用1.0.0+版本,兼容性更好):<!-- Spring AI 核心依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-deepseek</artifactId> <version>1.0.0</version> <!-- 请使用最新稳定版 --> </dependency>2. 编码实现:阻塞式与流式输出import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; import org.springframework.ai.deepseek.api.DeepSeekApi; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; public class DeepSeekSdkTest { // 初始化DeepSeek模型(核心对象) private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder() .deepSeekApi(DeepSeekApi.builder() .apiKey("你的API Key") // 替换为你的DeepSeek API Key .build()) .defaultOptions(DeepSeekChatOptions.builder() .model("deepseek-chat") // 指定模型(支持deepseek-chat、deepseek-coder等) .temperature(0.7) // 控制输出随机性(0-1,值越高越随机) .build()) .build(); // 测试1:阻塞式输出(适合简单场景,等待完整结果返回) @Test public void testBlockingCall() { // 构建对话消息(SystemMessage设定角色,UserMessage是用户提问) List<Message> messages = new ArrayList<>(); messages.add(new SystemMessage("你是一个出色的聊天助手,擅长通过幽默的风格回答用户问题。")); messages.add(new UserMessage("你好,请问你是谁")); // 调用模型并获取结果 ChatResponse response = chatModel.call(new Prompt(messages)); // 打印完整回复 System.out.println("完整回复:" + response.getResult().getOutput().getText()); } // 测试2:流式输出(适合用户体验场景,逐字显示回复) @Test public void testStreamCall() { // 准备参数 List<Message> messages = new ArrayList<>(); messages.add(new SystemMessage("你是一个出色的聊天助手,擅长通过幽默的风格回答用户问题。")); messages.add(new UserMessage("你好,请问你是谁")); // 调用 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); // 打印结果 flux.subscribe(resp -> System.out.println(resp.getResult().getOutput().getText())); // 阻塞等待(测试环境用,实际项目无需此操作) try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }代码说明DeepSeekChatModel:DeepSeek SDK 的核心类,通过建造者模式配置 API Key 和模型参数。call()方法:阻塞式调用,适合不需要实时显示的场景(如后端批量处理),结果如下图。stream()方法:流式调用,返回Flux对象,通过subscribe()实时接收输出(前端可配合 SSE 显示)。模型参数:temperature控制回复随机性(0 = 严谨,1= creative),model可切换为代码模型deepseek-coder。方式二:通过 OpenAI 兼容 SDK 调用(推荐!)大多数主流大模型(包括 DeepSeek)都兼容 OpenAI 的 API 格式,用 OpenAI 的 SDK 调用更通用,后续切换模型(如 GPT、 Claude)几乎不用改代码。1. 添加依赖引入 Spring AI 对 OpenAI 的支持(和方式一的依赖不冲突,可共存):<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> <version>1.0.0</version> <!-- 与DeepSeek依赖版本保持一致 --> </dependency>2. 编码实现:兼容 OpenAI 格式import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; public class OpenAiCompatibleTest { // 初始化OpenAI兼容模型(核心是替换baseUrl为DeepSeek) private final OpenAiChatModel chatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl("https://api.deepseek.com/") // DeepSeek的OpenAI兼容接口地址 .apiKey("你的API Key") // 同样使用DeepSeek的API Key .build()) .defaultOptions(OpenAiChatOptions.builder() .model("deepseek-chat") // 模型名称与DeepSeek一致 .temperature(0.7) .build()) .build(); // 测试阻塞式输出(和方式一用法几乎一致) @Test public void testBlockingCall() { List<Message> messages = new ArrayList<>(); messages.add(new SystemMessage("你是一个出色的聊天助手,擅长通过幽默的风格回答用户问题。")); messages.add(new UserMessage("你好,请问你是谁")); ChatResponse response = chatModel.call(new Prompt(messages)); System.out.println("完整回复:" + response.getResult().getOutput().getText()); } // 测试流式输出 @Test public void testStreamCall() { // 准备参数 List<Message> messages = new ArrayList<>(); messages.add(new SystemMessage("你是一个出色的聊天助手,擅长通过幽默的风格回答用户问题。")); messages.add(new UserMessage("你好,请问你是谁")); // 调用 Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); // 打印结果 flux.subscribe(resp -> System.out.println(resp.getResult().getOutput().getText())); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }为什么推荐这种方式?兼容性强:后续想切换到 GPT-4、Anthropic Claude 等模型,只需修改baseUrl和model参数。生态成熟:OpenAI 的 SDK 文档和社区支持更丰富,遇到问题更容易找到解决方案。代码复用:团队中如果已有 OpenAI 调用逻辑,无需重复开发,直接适配 DeepSeek。方式三:原生 HTTP 请求调用(无 SDK,更灵活)如果不想依赖第三方 SDK,也可以直接通过 HTTP 请求调用 DeepSeek 的 API,适合需要自定义请求 / 响应处理的场景。1. 核心原理DeepSeek 的聊天接口地址为:https://api.deepseek.com/v1/chat/completions,支持 POST 请求,通过stream: true参数开启流式输出。请求头需携带Authorization: Bearer 你的API Key,请求体为 JSON 格式(包含对话消息、模型参数等)。2. 编码实现:用 WebClient 发送请求import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; public class HttpDirectCallTest { // 你的DeepSeek API Key private static final String API_KEY = "你的API Key"; // DeepSeek聊天接口地址 private static final String API_URL = "https://api.deepseek.com/v1/chat/completions"; @Test public void testStreamHttpCall() { // 构建请求体JSON(流式输出需设置stream: true) String requestBody = """ { "messages": [ { "role": "system", "content": "你是一个出色的聊天助手,擅长通过幽默的风格回答用户问题。" }, { "role": "user", "content": "如何用Java发送HTTP请求?" } ], "model": "deepseek-chat", "stream": true, "temperature": 0.7, "max_tokens": 2048 } """; // 用WebClient发送POST请求(响应式HTTP客户端) WebClient webClient = WebClient.create(API_URL); Flux<String> responseFlux = webClient.post() .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + API_KEY) .bodyValue(requestBody) .retrieve() .bodyToFlux(String.class); // 流式接收响应 // 订阅并处理响应 responseFlux.subscribe( chunk -> { // 每一段流式输出(SSE格式,需解析data字段) if (chunk.contains("\"data\":")) { String content = chunk.split("\"data\":")[1].replace("}", "").replace("\"", "").trim(); if (!content.equals("[DONE]")) { // 过滤结束标识 System.out.print(content); } } }, error -> System.err.println("HTTP请求错误:" + error.getMessage()), () -> System.out.println("\nHTTP流式输出结束~") ); // 阻塞等待 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }代码说明请求体参数:messages是对话历史(role支持system/user/assistant),stream: true开启流式输出。响应处理:流式响应为 SSE(Server-Sent Events)格式,每一行是data: {内容},需解析data字段获取实际输出。灵活性:可自定义超时时间、重试机制、请求拦截器等,适合复杂场景(如企业级网关适配)。三种方式对比与选择建议方式优点缺点适合场景DeepSeek 官方 SDK原生支持,参数适配性好仅支持 DeepSeek,切换模型需改代码仅用 DeepSeek,快速集成OpenAI 兼容 SDK跨模型兼容,生态成熟需确认模型兼容性(部分参数可能不同)可能切换模型,追求代码复用原生 HTTP 请求无依赖,高度自定义需手动处理参数、解析响应,开发效率低自定义需求高,避免第三方 SDK 依赖👉 新手推荐方式二,兼顾简单性和灵活性;追求极致自定义用方式三;仅深度集成 DeepSeek 用方式一。避坑指南API Key 安全:不要硬编码 API Key,建议通过配置文件(如application.yml)或环境变量注入,避免泄露。超时设置:大模型响应可能较慢,需在 HTTP 客户端或 SDK 中设置合理超时时间(如 30 秒以上)。流式输出前端适配:如果前端需要显示流式效果,可通过 SSE(Server-Sent Events)或 WebSocket 接收后端的Flux输出。模型参数调试:temperature和top_p控制输出风格,可根据需求调整(技术问答建议temperature=0.3更严谨)。总结通过本文的 3 种方式,介绍了 Java 调用 DeepSeek 大模型的核心方法。无论是快速集成的 SDK 方式,还是灵活的原生 HTTP 请求,都能满足不同场景的需求。接下来可以尝试扩展功能:比如添加对话历史管理、实现多轮对话,或者集成到 Spring Boot 项目中提供 API 接口。如果遇到问题,欢迎在评论区留言, 祝大家开发顺利! 🚀
2025年08月11日
28 阅读
0 评论
0 点赞
1
2
3
...
5