springboot3.2使用dashscope-sdk-java调用阿里千问大模型,流式回答 作者:马育民 • 2026-04-11 16:03 • 阅读:10002 # 技术选型 spring-ai更新太快,api不统一,截止到目前为止,不适合企业开发 目前企业开发中,还是使用 `dashscope-sdk-java` 的 `2.16.x` 版本更多 # 依赖 排除 `slf4j-simple` 依赖,否则启动有警告或报错 ``` com.alibaba dashscope-sdk-java 2.16.10 org.slf4j slf4j-simple ``` # 大模型配置 配置 `api-key` 和 模型名称 ``` # DashScope AI配置 dashscope: api-key: your-api-key-here model: qwen-turbo ``` # 授权过滤 如果使用 **流式回答**,必须放开授权,否则会被拦截 ``` @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // 禁用 CSRF 保护,因为我们使用 JWT 认证 .csrf(AbstractHttpConfigurer::disable) // 配置请求授权 .authorizeHttpRequests(authorize -> authorize // 允许登录接口匿名访问 .requestMatchers("/api/users/login").permitAll() .requestMatchers("/app/login").permitAll() // 放行 ai 的url .requestMatchers("/api/ai/stream").permitAll() // 其他所有请求都需要认证 .anyRequest().authenticated() ) // 添加 JWT 过滤器 .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } ``` # Controller实现 ``` package com.smartagriculture.controller; 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.ResultCallback; import com.alibaba.dashscope.common.Role; import com.smartagriculture.utils.JwtUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.util.ArrayList; import java.util.Collections; /** * AI助手控制器 */ @RestController @RequestMapping("/api/ai") public class AIController { private static final Logger logger = LoggerFactory.getLogger(AIController.class); private static final String PROMPT_SUFFIX = "不要用markdown格式"; @Autowired private JwtUtil jwtUtil; @Value("${dashscope.api-key}") private String configApiKey; @Value("${dashscope.model}") private String model; @GetMapping("/stream") public SseEmitter streamAIResponse(@RequestParam String prompt, @RequestHeader(value = "Authorization", required = false) String tokenHeader) { logger.info("收到AI请求,prompt: {}", prompt); SseEmitter emitter = new SseEmitter(); // ====================== // 手动鉴权(安全核心) // ====================== int check = check(tokenHeader); if (check == -1) { emitter.completeWithError(new RuntimeException("无权限访问")); return emitter; }else if (check == -2) { emitter.completeWithError(new RuntimeException("token 无效")); return emitter; } // 异步线程处理 new Thread(() -> { try { GenerationParam param = GenerationParam.builder() .apiKey(configApiKey) .model(model) .messages(Collections.singletonList( Message.builder() .role(Role.USER.getValue()) .content(prompt + PROMPT_SUFFIX) .build() )) .incrementalOutput(true) .build(); // 🔥 使用独立的回调类(无Lambda) Generation generation = new Generation(); generation.streamCall(param, new AiStreamResultCallback(emitter)); } catch (Exception e) { logger.error("AI接口调用失败", e); emitter.completeWithError(e); } }).start(); return emitter; } private int check(String token){ logger.debug("JWT Filter: Authorization header = " + token); // 检查 token 是否存在且格式正确 if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); // 去除 "Bearer " 前缀 logger.debug("JWT Filter: Token = " + token); // 验证 token if (jwtUtil.validateToken(token)) { // 从 token 中获取用户名 String username = jwtUtil.getUsernameFromToken(token); logger.debug("JWT Filter: Username from token = " + username); // 创建用户详情对象 UserDetails userDetails = new User(username, "", new ArrayList<>()); // 创建认证对象 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); // 设置认证信息到 SecurityContext return 0; } else { logger.info("JWT Filter: Invalid token"); return -2; } } else { return -1; } } public static class AiStreamResultCallback extends ResultCallback { private final SseEmitter emitter; public AiStreamResultCallback(SseEmitter emitter) { this.emitter = emitter; } /** * 流式接收消息 🔥 2.16 标准写法 */ @Override public void onEvent(GenerationResult result) { try { if (result == null || result.getOutput() == null) { return; } // 企业标准写法,永不null String content = result.getOutput().getText(); if (content != null && !content.isEmpty()) { logger.warn(content); emitter.send(content); } } catch (Exception e) { emitter.completeWithError(e); } } /** * 流式完成 */ @Override public void onComplete() { try { emitter.send("[DONE]"); emitter.complete(); } catch (Exception e) { emitter.completeWithError(e); } } /** * 异常处理 */ @Override public void onError(Exception e) { logger.error("AI流式回调异常", e); emitter.completeWithError(e); } } } ``` 原文出处:http://malaoshi.top/show_1GW371BkKVSJ.html