LangChain 1.0 升级全解:从 createReactAgent 到 createAgent 的一场「重构」

LangChain 1.0 终于出了。官方的定位很明确:

"LangChain v1 is a focused, production-ready foundation for building agents."

这次升级不是简单的 API 换个名字,而是把过去一整年踩坑总结的经验,重新压缩成一套以 agent 为中心的核心能力:

  • createAgent:统一的 Agent 构建入口
  • ✅ Middleware:横切能力(日志、HITL、摘要、安全...)的标准化
  • ✅ Structured Output + Standard Content:结构化输出 & 多模态内容统一抽象
  • ✅ 精简包结构:langchain 专注 agent,历史包迁到 @langchain/classic (LangChain 文档)

这篇文章会完全基于 LangChain 官方文档,按照 JS v1 migration guide 的结构,把每一条变化讲清楚,再加上一些自己的理解,帮你从 0.0.x 的世界平稳过渡到 1.0。


一、核心变化概览:从表格看 v1 心智模型

JS 版 v1 migration guide 一上来就给了一个总表,基本涵盖了所有关键改动:(LangChain 文档)

主题 v1 里"变了什么"
Import path 预构建 agent 从 @langchain/langgraph/prebuilts 移到 langchain
Prompts prompt 改名 systemPrompt;动态 prompt 用 middleware 实现
Pre/Post-model hook 统一变成 middleware 的 beforeModel / afterModel
Custom state 用 middleware 的 stateSchema + Zod 定义
Model 动态模型选择挪到 wrapModelCall;不再推荐"预先 bind 工具的 model"
Tools tools 接收 tool()、Tool 实例和 provider 内建工具;错误交给 wrapToolCall
Structured output 内联到主循环,新增 toolStrategy / providerStrategy
Streaming 流式事件中的节点名从 "agent" 改为 "model"
Runtime context 配置改用 context 字段,不再鼓励 config.configurable
Standard content 新增 message.contentBlocks 标准内容块
Namespace langchain 精简为 agent 核心能力,其余迁到 @langchain/classic
Breaking changes Node 18 下线、新打包输出、旧 API 移除等

下面我按这些主题,一块块展开。


二、createAgent:统一的 Agent 入口 + Middleware 基础设施

2.1 从 createReactAgent 到 createAgent

旧世界里,你如果用的是 LangGraph 的预构建 ReAct agent,一般是这样引:(LangChain 文档)

typescript 复制代码
// v0
import { createReactAgent } from "@langchain/langgraph/prebuilts";

在 v1 里,它变成了 LangChain 的一等公民:

typescript 复制代码
// v1
import { createAgent } from "langchain";

const agent = createAgent({
  model: "claude-sonnet-4-5-20250929",
  tools: [getWeather],
  systemPrompt: "You are a helpful assistant.",
});

我的理解:

  • 以前"agent"是 LangGraph 的预构建,现在官方把这个预构建接回 LangChain,使得:
    • LangChain = 高层 API(createAgent)
    • LangGraph = 低层 graph runtime
  • 不再需要先学 LangGraph 才能用一个像样的 agent,但仍然可以随时"下潜"到 LangGraph 做高级控制。

2.2 Prompt:promptsystemPrompt + 动态 prompt 中间件

文档里把 prompt 的变化拆成三块:静态重命名、SystemMessage、动态 prompt。(LangChain 文档)

2.2.1 静态 system prompt 重命名

  • 旧:prompt
  • 新:systemPrompt
typescript 复制代码
const agent = createAgent({
  model,
  tools,
  systemPrompt: "You are a helpful assistant.",
});

为什么要改名?

  • prompt 这个词太宽泛,谁都可以叫 prompt;
  • systemPrompt 明确告诉你:这只是系统级前置指令,用户 history / 工具反馈等都是别的层处理。

2.2.2 SystemMessage 行为简化

文档提到:如果你在 system prompt 中仍然用 SystemMessage,v1 会直接取其中的 string 内容 ,不再绕一圈。(LangChain 文档)

这其实是在消除"一堆 message 对象嵌套在一起"的复杂度,把 system 指令都归一成纯文本,便于统一注入到模型的 system 部分。

2.2.3 动态 prompt:交给 middleware 处理

动态 prompt(根据上下文、用户角色、状态动态改 system prompt)被认定为"核心 context engineering 模式",官方给了专门的 middleware:dynamicSystemPromptMiddleware (LangChain 文档)

示意代码(截取一下重点):

typescript 复制代码
const contextSchema = z.object({
  userRole: z.enum(["expert", "beginner"]).default("beginner"),
});

const userRolePrompt = dynamicSystemPromptMiddleware(
  (_state, runtime) => {
    const userRole = runtime.context.userRole;
    const base = "You are a helpful assistant.";
    if (userRole === "expert") return `${base} Provide detailed technical responses.`;
    if (userRole === "beginner") return `${base} Explain concepts simply and avoid jargon.`;
    return base;
  }
);

const agent = createAgent({
  model,
  tools,
  middleware: [userRolePrompt],
  contextSchema,
});

我的理解:

  • 动态 prompt 本质上是一个典型的 cross-cutting concern(横切关注点):它会影响所有 model 调用;
  • v1 把它收敛到 middleware,而不是随便塞在 agent 逻辑里;
  • 和你之前写的「按用户角色切换系统提示」需求,完美对上号。

2.3 Pre-model / Post-model hook → beforeModel / afterModel middleware

以前的"前/后 hook"现在都统一到 middleware 的两个钩子里:(LangChain 文档)

旧名 新名 位置
pre-model hook beforeModel middleware
post-model hook afterModel middleware

2.3.1 beforeModel:模型调用前做的事

典型用途(文档列了三个):(LangChain 文档)

  • 摘要对话历史
  • 修剪消息(trim)
  • 输入安全(比如 PII 脱敏)

官方内置了一个 summarizationMiddleware,用法你已经看过:

typescript 复制代码
const agent = createAgent({
  model: "claude-sonnet-4-5-20250929",
  tools,
  middleware: [
    summarizationMiddleware({
      model: "claude-sonnet-4-5-20250929",
      trigger: { tokens: 1000 },
    }),
  ],
});

我的理解:

  • 以前你可能会在每次调用前手动 slice messages 或自己维护 session summary;
  • 现在这些都变成"插一个 middleware 就行"的模式;
  • 逻辑上属于"请求前过滤 / 缩减 / 丰富"的一类,非常适合做 cross-cutting。

2.3.2 afterModel:模型返回后做的事

典型用途:(LangChain 文档)

  • Human-in-the-loop(人审)
  • 输出安全(敏感内容过滤)

内置例子是 humanInTheLoopMiddleware,你之前已经研究过:

typescript 复制代码
const agent = createAgent({
  model: "claude-sonnet-4-5-20250929",
  tools: [readEmail, sendEmail],
  middleware: [
    humanInTheLoopMiddleware({
      interruptOn: {
        sendEmail: { allowedDecisions: ["approve", "edit", "reject"] },
      },
    }),
  ],
});

我的理解:

  • 所有"模型说完话之后还要人兜底"的逻辑(HITL、guardrail、合规审计),都归到 afterModel
  • 在流式/长流程 agent 中,这样的 hook 可以叠加多个,把风险逻辑模块化。

2.4 Custom state:stateSchema + middleware,别再塞到 createAgent

文档写得很清楚:

"Custom state is now defined in middleware using the stateSchema property. Use Zod to declare additional state fields that are carried through the agent run." (LangChain 文档)

示例(节选):

typescript 复制代码
const UserState = z.object({
  userName: z.string(),
});

const userState = createMiddleware({
  name: "UserState",
  stateSchema: UserState,
  beforeModel: (state) => {
    const name = state.userName;
    // ...根据 name 动态改 prompt 或做别的事
  },
});

我的理解:

  • v0 时,custom state 一般是在 createAgent 上用 stateSchema 一次性声明;
  • v1 更推荐:谁需要 state,就在哪个 middleware 上声明 stateSchema
  • 好处:
    • 状态和功能天然绑定(一个中间件就认领自己那块状态);
    • 重用中间件时,不会往全局 state 里乱加字段;
    • TypeScript / Zod 类型也更干净。

Python 那边也同步要求:自定义 state 必须是 TypedDict,而不再接受 Pydantic / dataclass,理由也是为了简单一致。


2.5 模型相关:动态选型 & 不再支持"预先绑定工具的 Model"

2.5.1 动态模型选择:用 wrapModelCall

旧时代,你可能在 model 参数里传一个函数,根据输入动态选择模型。v1 把这个逻辑统一搬进 middleware:(LangChain 文档)

typescript 复制代码
const dynamicModel = createMiddleware({
  name: "DynamicModel",
  wrapModelCall: (request, handler) => {
    const count = request.state.messages.length;
    const model = count > 10 ? "openai:gpt-5" : "openai:gpt-5-nano";
    return handler({ ...request, model });
  },
});

我的理解:

  • "按对话长度 / 用户付费等级 / 请求类型,动态切模型"是典型 cross-cutting concern;
  • 统一放到 wrapModelCall,你就可以:
    • 在同一个 agent 上轻松开关不同"模型策略";
    • 做 AB test / 灰度发布时只换一个 middleware。

2.5.2 不要再传"已经 bind 工具的 model"

文档原话:(LangChain 文档)

为了更好支持 structured output,createAgent 应该接收"纯 model + 单独的 tools 列表",不要再用事先 .bindTools(...) 的 model。

旧写法:

typescript 复制代码
// ❌ v1 不再支持
const modelWithTools = new ChatOpenAI({ model: "gpt-4o-mini" }).bindTools([someTool]);
const agent = createAgent({ model: modelWithTools, tools: [] });

新写法:

typescript 复制代码
// ✅ 推荐
const agent = createAgent({ model: "gpt-4o-mini", tools: [someTool] });

我的理解:

  • 结构化输出(尤其是用 ToolStrategy 时)的内部实现,需要框架统一管理"这次 LLM 调用到底有哪些 tools";
  • 如果你传进来的是"外面已经 bind 过 tools 的 model",LangChain 很难在内部安全地追加/调整工具列表;
  • 所以强制你:model 只负责"哪一家的哪个版本",tools 列表交给 createAgent

2.6 Tools:三种来源 + 错误处理统一用 wrapToolCall

2.6.1 tools 支持的三种形态

v1 中,createAgenttools 参数可以接:(LangChain 文档)

  1. tool() 包出来的函数(最推荐)
  2. LangChain Tool 实例
  3. Provider 内建工具的"配置对象"

这让你可以写出很统一的代码:

typescript 复制代码
const search = tool(
  ({ query }) => `Results for: ${query}`,
  {
    name: "search",
    description: "Search the web",
    schema: z.object({ query: z.string() }),
  }
);

const agent = createAgent({
  model: "gpt-4o",
  tools: [search, dbTool, openAIFileSearchTool],
});

我的理解:

  • 不管工具是你自己写的、从 @langchain/community 拿的、还是 provider 内建的,现在都能放进同一个 tools 数组里统一调度;
  • 这也是为后面 llmToolSelectorMiddleware 等中间件做铺垫。

2.6.2 工具错误处理:wrapToolCall 中间件

文档里专门有一小节:Handling tool errors ,推荐用 wrapToolCall 中间件来统一兜错误:(LangChain 文档)

官方示例思路是:

typescript 复制代码
const handleToolErrors = createMiddleware({
  name: "HandleToolErrors",
  wrapToolCall: async (request, handler) => {
    try {
      return await handler(request);
    } catch (error) {
      return new ToolMessage({
        content: `Tool error: Please check your input and try again. (${error})`,
        tool_call_id: request.toolCall.id!,
      });
    }
  },
});

我的理解:

  • 不用在每个 tool 里都写 try/catch,然后返回一堆"错误字符串";
  • 工具真报错时,转成一条 ToolMessage 回给模型,让模型自己决定:
    • 重试?
    • 换工具?
    • 直接向用户道歉?
  • 工具错误处理也被归类为 cross-cutting concern,交给 middleware 这一层统一管理。

2.7 Runtime context:告别 config.configurable

v1 引入了 context 配置:(LangChain 文档)

typescript 复制代码
const agent = createAgent({
  model: "gpt-4o",
  tools,
  contextSchema: z.object({ userId: z.string(), sessionId: z.string() }),
});

const result = await agent.invoke(
  { messages: [new HumanMessage("Hello")] },
  { context: { userId: "123", sessionId: "abc" } },
);

而以前你可能是这样:

typescript 复制代码
agent.invoke(input, { configurable: { userId: "123" } });

我的理解:

  • config.configurable 这个名字又长又模糊;
  • context 则一眼能看出:这是"这次执行的上下文配置",只读、静态;
  • 在 middleware 的 runtime.context 里,你也能统一访问这些字段(比如 userRole、tenantId、业务开关等)。

2.8 Streaming node name:"agent""model"

这个细节很小,却很容易踩坑:

"When streaming events from agents, the node name was changed from "agent" to "model"." (LangChain 文档)

如果你有在前端 / 后端按 node 名过滤流式事件,记得从:

typescript 复制代码
if (event.name === "agent") { ... }

改成:

typescript 复制代码
if (event.name === "model") { ... }

原因也很直白:这个 node 实际做的,就是"调用模型",叫 "agent" 容易和整个系统混淆。


三、Structured Output:主循环内联 + ToolStrategy / ProviderStrategy

结构化输出这块,文档重点讲了三件事:(LangChain 文档)

  1. Node 变化:不再单独一个 node,多余的一次 LLM 调用被干掉
  2. 策略:toolStrategy & providerStrategy
  3. Prompted output 移除:禁止"单纯用 prompt 硬控格式"

3.1 Node 层面的变化:不再多调一次 LLM

原文:

"Structured output used to be generated in a separate node... Structured output is generated in the main loop (no extra LLM call), reducing cost and latency." (LangChain 文档)

以前:

  • agent 主循环跑完,再搞一个"结构化输出 node";
  • 等于:多调一次模型,把自然语言再转成结构化 JSON。

现在:

  • 结构化输出直接在 agent 的主循环内部完成;
  • 因此:
    • 延迟降低;
    • 费用降低;
    • 代码逻辑也简单了(不再有"最后多一个 node"的心智负担)。

3.2 两种策略:ToolStrategy vs ProviderStrategy

文档里明确区分了两种 structured output 策略:(LangChain 文档)

  • toolStrategy(schema)
    → 用"人工工具调用"来让模型按 schema 输出(即我们之前聊的"伪造一个结构化输出工具")
  • providerStrategy(schema)
    → 用模型提供商原生的 JSON Schema / 结构化输出能力(比如 OpenAI 的 .response_format

示例(toolStrategy):

typescript 复制代码
import { createAgent, toolStrategy } from "langchain";
import * as z from "zod";

const OutputSchema = z.object({
  summary: z.string(),
  sentiment: z.string(),
});

const agent = createAgent({
  model: "gpt-4o-mini",
  tools,
  responseFormat: toolStrategy(OutputSchema),
});

更简单的情况:你也可以直接传 Zod schema,LangChain 会根据当前模型是否支持原生结构化输出自动选择策略:

typescript 复制代码
const agent = createAgent({
  model: "gpt-4o-mini",
  tools,
  responseFormat: weatherSchema, // 自动选策略
});

我的理解:

  • toolStrategy 更通用,只要模型支持 tool calling 就能用;
  • providerStrategy 更严格,依赖具体 provider 的 JSON-schema 能力;
  • v1 把这俩抽象成"策略对象",而不是让你手写"怪异的 responseFormat 字符串"。

3.3 Prompted output 被移除:不再鼓励"靠 prompt 硬控 JSON"

文档开门见山:

"Prompted output via custom instructions in responseFormat is removed in favor of the above strategies." (LangChain 文档)

之前:

  • 有人会在 responseFormat 里塞一段说明:"请按以下 JSON 返回......",本质上只是多了点 prompt;
  • 不稳定、难校验------一旦模型输出带多余文本 / markdown,就解析炸。

现在:

  • 要结构化输出,就明确选策略 + schema;
  • 返回结果统一出现在 agent state 的 structuredResponse 里,并且经过 Zod / TypedDict 校验。

四、Standard Content Blocks:跨厂商统一的消息内容模型

这是 v1 另一个非常重要但很容易忽视的改动:标准内容块(standard content blocks)

4.1 问题:不同 provider 的消息格式结构各不相同

现代 LLM 输出已经不是单纯一串文本,而是:

  • reasoning / thinking 块
  • citations(来源引用)
  • 多模态(image/audio/video)
  • 内置工具调用结果(代码执行、搜索等)

每家 provider 的结构都不一样,比如:

  • Anthropic 的 content block 有 thinking type;
  • OpenAI 自己有一套 annotations 格式;
  • Amazon Nova 也有自定义的 content block 结构。

如果你直接用 message.content,就会变成各种 if(provider === "anthropic") 的地狱。

4.2 v1:message.contentBlocks 标准化视图

JS 迁移文档里写得很清楚:(LangChain 文档)

  • 新增 contentBlocks 属性:provider-agnostic 标准内容块
  • 新增 ContentBlock.* TypeScript 类型
  • 可以通过环境变量 / 配置把 standard block 序列化回 content

使用方式:

typescript 复制代码
const model = await initChatModel("gpt-5");
const response = await model.invoke("Explain AI");

for (const block of response.contentBlocks) {
  if (block.type === "reasoning") {
    console.log(block.reasoning);
  } else if (block.type === "text") {
    console.log(block.text);
  }
}

构造多模态输入:

typescript 复制代码
const message = new HumanMessage({
  contentBlocks: [
    { type: "text", text: "Describe this image." },
    { type: "image", url: "https://example.com/image.jpg" },
  ],
});
const res = await model.invoke([message]);

我的理解:

  • contentBlocks 就是"跨 provider 的统一 AST";
  • 如果你要做:
    • 展示 reasoning trace;
    • 统一 UI 上的图文混排;
    • 把 OpenAI / Anthropic / Nova 的输出全部喂给前端;
  • 那就应该用 contentBlocks,而不是继续手搓解析各家的原始 content 结构。

4.3 content 还在吗?如何序列化 standard blocks?

文档强调:

"The existing message.content field remains unchanged for strings or provider-native structures." (LangChain 文档)

也就是说:

  • 老代码里直接 msg.content 拿字符串的逻辑不会被破坏;
  • 新的 contentBlocks额外的一层视图,需要时再用。

如果你想把 standard block 序列化回 content(比如要通过网络传给前端,只想看一个字段),可以设置:(LangChain 文档)

bash 复制代码
export LC_OUTPUT_VERSION=v1
# 或在 JS 初始化模型时 outputVersion: "v1"

五、包结构精简:langchain & @langchain/classic

5.1 langchain:只做 agent 核心

v1 概览文档和 migration guide 一致强调:

"The langchain package namespace is streamlined to focus on agent building blocks."

目前 JS 版 langchain 暴露的内容主要包括:

  • Agents:createAgent, AgentState
  • Messages:消息类型、trimMessages、content blocks(re-export 自 @langchain/core
  • Tools:tool + 部分工具基类
  • Models:initChatModel, BaseChatModel

我的理解:

  • langchain 现在更像一个"agents SDK",聚焦在:
    • 接模型
    • 接工具
    • 用 middleware 拼装逻辑
  • 其他传统功能(chains、retrievers、indexing...)都挪到 classic 包。

5.2 @langchain/classic:老世界的"博物馆"

文档说明:

如果你还在用:

  • 传统 chains
  • indexing API
  • 原来从 @langchain/community re-export 的东西

现在请安装 @langchain/classic,从那里 import。

迁移方式大致是:

typescript 复制代码
// v0
import { stuffDocumentsChain } from "langchain/chains";

// v1
import { stuffDocumentsChain } from "@langchain/classic/chains";

我的理解:

  • @langchain/classic 有点像"LTS 老版本":
    • 功能保留;
    • 发布节奏相对稳定;
    • 但不再是"未来重点投资源的地方",新特性基本都围绕 agent / middleware / content blocks 展开。
  • 对你自己项目来说:
    • 新东西尽量用 langchain + @langchain/core
    • 老代码可以渐进式迁移,一时半会迁不动就挂在 @langchain/classic 上继续用。

5.3 删除的废弃 API

migration guide 还给了一大串已经被删掉的 API 列表,比如:

  • Core:
    • TraceGroup → 用 LangSmith tracing
    • RemoteRunnable → 直接砍掉
  • Runnables:
    • Runnable.bind → 改用 .bindTools() 等专门方法
    • Runnable.map → 用 .batch()
  • Chat models / LLM:
    • predict, predictMessages, call 等 → 全部用 .invoke() 统一起来
  • Retrievers:
    • getRelevantDocuments.invoke()

我的理解:

  • .invoke() 成为统一调用入口,是 LangChain 这两年一直在做的"API 收敛";
  • 如果你现在还在到处 predictcallgetRelevantDocuments,v1 是非常好的时机统一清理。

六、平台 & 构建层:Node 20、Bundler 输出、新的 release 政策

6.1 Node 18 下线,Node 20+ 才能用

JS 侧 changelog 和 migration guide 都写了:

所有 LangChain 包现在都要求 Node.js 20 或更高版本。Node 18 已在 2025 年 3 月 EOL。

这意味着:

  • 在你的项目里,如果还锁在 Node 18,升级 LangChain 1.0 会直接失败;
  • 建议顺手也把其它依赖检查一下(比如 Vite、Next.js 等),一并升级到 Node 20 系列。

6.2 新的构建输出:从 tsc 散装文件到 bundler

原文:

"Builds for all langchain packages now use a bundler based approach instead of using raw typescript outputs. If you were importing files from the dist/ directory... you will need to update your imports to use the new module system."

简单翻译一下:

  • 以前:src → tsc → dist,目录结构几乎一样,你可以(虽然不推荐):
typescript 复制代码
import { X } from "langchain/dist/whatever/internal.js";
  • 现在:
    • 用 bundler(tsup / rollup 那一流)根据少数入口打包;
    • dist/ 不再对应源码结构,很多内部文件路径直接消失;
    • 必须只通过官方导出的模块入口来用包。

我的理解:

  • 这是在从工程上彻底扼杀"深度 import 内部文件"的行为(不然一升级就爆);
  • 同时也带来了更好的 tree-shaking 和多环境兼容,对前端 bundle 体积是利好。

七、如何实际迁移自己的项目?

最后给一个实战向的 checklist,方便你把这篇博客当"迁移 todo list"。

  1. 升级依赖 & Node 版本
    • Node 升到 20+;
    • langchain & @langchain/core 升到最新 v1;(LangChain 文档)
  2. **把 createReactAgent 换成 **createAgent
    • import 路径改到 "langchain"
    • 把原来传给 model 的动态函数、pre/post hook,迁到 middleware 上;
    • prompt 改为 systemPrompt,动态 prompt 用 dynamicSystemPromptMiddleware
  3. 工具相关
    • 不再使用 model.bindTools() 传进 agent;
    • 把所有工具放进 tools: [...]
    • 如果有统一的错误兜底逻辑,用一个 wrapToolCall 中间件代替散装 try/catch。
  4. 结构化输出
    • 如果以前用的是 "prompt 里自己写 JSON 规范 + 手动 parse",现在改:
      • responseFormat: zodSchema
      • responseFormat: toolStrategy(schema)
    • 从返回的 structuredResponse 里取结果。
  5. 消息 & 多模态
    • 如果开始接入 reasoning、多模态、内置工具结果:统一用 contentBlocks
    • 给前端传数据时,如果只想看到一个字段,可以通过 LC_OUTPUT_VERSION=v1 把 block 序列化进 content
  6. 包结构 & 旧功能
    • 任何 langchain/chains 之类的 import,都迁到 @langchain/classic
    • 把所有 .predict / .call / .getRelevantDocuments 改成 .invoke()
  7. Streaming / 监控
    • 如果在用 streamEvents() 或类似接口,记得把 node 名从 "agent" 改成 "model"
    • tracing 相关 API 改用 LangSmith。

总结:LangChain 1.0 是一次「收敛」而不是「推翻重来」

从官方的 release 文档来看,v1 的主线非常清晰:

  • Agent 是一等公民:createAgent + LangGraph runtime
  • Middleware 成为承载 cross-cutting concern 的标准机制
  • Structured output 和 Standard content 负责把"模型输出 → 应用可用数据"的链路拉直
  • 包结构和构建方式收敛到一个更可维护、更稳定的形态

如果你这段时间一直在围绕「Agent + 工具 + 上下文 + 人审 + 安全」做事情,那 v1 基本就是把你常写的那堆"胶水逻辑",正式升级为框架的 first-class feature。

相关推荐
想摆烂的不会研究的研究生6 小时前
每日八股——Redis(1)
数据库·经验分享·redis·后端·缓存
毕设源码-郭学长7 小时前
【开题答辩全过程】以 基于SpringBoot技术的美妆销售系统为例,包含答辩的问题和答案
java·spring boot·后端
追逐时光者7 小时前
精选 10 款 .NET 开源免费、功能强大的 Windows 效率软件
后端·.net
追逐时光者7 小时前
一款开源、免费的 WPF 自定义控件集
后端·.net
S***q3778 小时前
Spring Boot管理用户数据
java·spring boot·后端
毕设源码-郭学长8 小时前
【开题答辩全过程】以 基于SpringBoot框架的民俗文化交流与交易平台的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
l***21789 小时前
SpringBoot Maven快速上手
spring boot·后端·maven
f***14779 小时前
SpringBoot实战:高效实现API限流策略
java·spring boot·后端
计算机毕设VX:Fegn08959 小时前
计算机毕业设计|基于springboot + vue动物园管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计