<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>未读代码 Feed</title>
    <link rel="alternate" type="text/html" href="https://www.wdbyte.com/"/>
    <link rel="self" type="application/atom+xml" href="https://www.wdbyte.com/atom.xml"/>
    <id>tag:www.wdbyte.com,/</id>
    <updated>2026-01-22T00:00:00Z</updated>
    <subtitle>程序猿阿朗</subtitle>
    <entry>
        <title>MCP Streamable HTTP &#x534f;&#x8bae;&#x5165;&#x95e8;&#x4e0e; 100 &#x884c;&#x4ee3;&#x7801;&#x5b9e;&#x73b0;</title>
        <link rel="alternate" type="text/html" href="https://www.wdbyte.com/spring-mcp-server-manual/"/>
        <id>tag:www.wdbyte.com,/spring-mcp-server-manual/</id>
        <published>2026-01-22T00:00:00Z</published>
        <updated>2026-01-22T00:00:00Z</updated>
        <summary>&#x4ecb;&#x7ecd; MCP &#x534f;&#x8bae;&#x4e2d; Streamable HTTP &#x4f20;&#x8f93;&#x673a;&#x5236;&#x7684;&#x4e00;&#x4e9b;&#x5177;&#x4f53;&#x5185;&#x5bb9;&#xff0c;&#x7136;&#x540e;&#x624b;&#x52a8;&#x5b9e;&#x73b0;&#x4e00;&#x4e2a;&#x7b80;&#x6613;&#x7248;&#x7684; MCP Streamable HTTP &#x670d;&#x52a1;&#xff0c;&#x53ea;&#x9700;&#x8981;100&#x884c;&#x4ee3;&#x7801;&#x3002;</summary>
        <author>
            <name>程序猿阿朗</name>
            <uri>https://www.wdbyte.com</uri>
        </author>
        <content type="html" xml:lang="en" xml:base="https://www.wdbyte.com/"><![CDATA[为了更好的阅读体验，<a href="https://www.wdbyte.com/spring-mcp-server-manual/">可以点击跳转到网页继续阅读.....</a></b><p>最近在开发 MCP 服务，服务上线后，想到不使用 SDK 实现 MCP 服务是否困难，因此有了这篇文章，这篇文章会介绍 MCP 协议中 Streamable HTTP 传输机制的一些具体内容，然后手动实现一个简易版的 MCP Streamable HTTP 服务，只需要100行代码。</p>
<h2 id="mcp-简介">MCP 简介</h2>
<p><strong>MCP</strong>（Model Context Protocol）模型上下文协议是一个基于 JSON-RPC 的开放协议，它提供了一种标准化的方式，让 LLM 与所需服务交互，实现 LLM 应用与外部数据源及工具之间的无缝集成。无论是在构建 AI 驱动的 IDE、AI 聊天界面，还是创建自定义 AI 工作流程、构建 AI 应用，都极大地降低了对接复杂度。</p>
<p>MCP 兼顾了标准化与灵活性。如对基础交互的消息类型、生命周期的初始化、能力协商、会话管理、优雅关闭，服务功能的暴露通知方式都有明确的定义。</p>
<p>说简单点就是：为 LLM 应用与服务之间提供了一个使用 JSON 格式交互的统一规范。大家都遵循这个规范，LLM 应用对接外部服务就变得简单。</p>
<h2 id="协议基本规范">协议基本规范</h2>
<p>MCP 协议定义了 <a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/transports?ref=wdbyte.com#stdio" target="_blank">STDIO</a> 和 <a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/transports?ref=wdbyte.com#streamable-http" target="_blank">Streamable HTTP</a> 两种标准传输机制。STDIO 运行在本地，Streamable HTTP 可以是远程的 HTTP 服务，这篇文章围绕 Streamable HTTP 方式进行介绍。</p>
<p>按照 MCP Streamable HTTP 的生命周期要求，执行一个 Tools 调用的完整最小链路为：</p>
<ol>
<li>连接初始化：initialize</li>
<li>连接建立完成通知：notifications/initialized</li>
<li>获取工具列表：tools/list</li>
<li>调用一个工具：tools/call。</li>
</ol>
<p>这其中 <code>tools/list</code> 非强制选项，如果已经提前缓存了 <code>tools/list</code> ，可以直接发起调用，但在动态环境中建议先使用 <code>tools/list</code> 以确保调用合法性。</p>
<p>需要注意的是：MCP Streamable HTTP 可以通过 Session ID 维护状态，但是这是可选项，本文后续默认使用无状态 Streamable HTTP 进行介绍。</p>
<h3 id="mcp-streamable-http-api">MCP Streamable HTTP API</h3>
<p>MCP 协议使用 JSON-RPC 对消息进行编码。同时采用 UTF-8 编码。MCP 服务必须提供 HTTP API，同时支持 POST 和 GET 请求（但仅 POST 用于 RPC）。例如：<a href="https://example.com/mcp?ref=wdbyte.com" target="_blank">https://example.com/mcp</a> 这个 URL。客户端会使用 POST 向 MCP API 发起请求。</p>
<h3 id="client-request">Client Request</h3>
<p>方法与格式：客户端必须使用 HTTP POST 向 MCP 端点发送消息，内容必须是单一的 JSON-RPC 请求、通知或响应。</p>
<p>请求体 Body 的基本格式如下：</p>
<pre><code class="language-json">{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 1,
  &quot;method&quot;: &quot;tools/call&quot;,
  &quot;params&quot;:{
    //...
  }
}
</code></pre>
<p>其中 <code>jsonrpc</code> 指定 RPC 版本，<code>id</code> 为本次会话中唯一的消息 id，可以是字符串也可以是数字。<code>method</code> 值为请求的操作，如 <code>tools/list</code>、<code>tools/call</code> 、<code>ping</code> 等。<code>params</code> 是可选的，如果是 <code>method=tools/call</code>，则 <code>params</code> 传入工具调用参数。</p>
<p>另外请求必须在 Accept 标头中列出 <code>application/json </code>和 <code>text/event-stream</code>。</p>
<p>请求头示例：</p>
<pre><code>Accept: application/json, text/event-stream
</code></pre>
<h3 id="server-response">Server Response</h3>
<p>MCP Streamable HTTP 服务端处理通知请求时，如客户端发送连接建立完成通知，服务端则接受后返回 202 Accepted，无正文；若不接受则返回 HTTP 错误码（如 400）。</p>
<p>若客户端发送的是其他请求，如工具调用。服务器根据操作耗时决定响应模式，<strong>快速操作</strong>（Quick operations）通常直接返回 JSON 响应，而<strong>耗时操作</strong>（Long-running tasks）或需要服务器主动推送消息时，会切换为 SSE 流（text/event-stream）</p>
<p><code>tools/call</code> 响应示例：</p>
<pre><code class="language-json">{
    &quot;id&quot;: 3,
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;result&quot;: {
      //...
    }
}
</code></pre>
<p>为了兼容性，客户端需能够同时支持处理上述两种响应格式，也就是普通 JSON 响应和 SSE 流。</p>
<h2 id="mcp-streamable-http-生命周期">MCP Streamable HTTP 生命周期</h2>
<p>已知在 MCP Streamable HTTP 机制下，执行一个 Tools 调用的最小链路为：initialize -&gt; notifications/initialized -&gt; tools/list -&gt; tools/call。下面会重点介绍一些这几个生命周期阶段的具体协议内容。</p>
<h3 id="初始化-initialize">初始化 initialize</h3>
<p>在交互之前，客户端必须先发送 <code>initialize</code>与服务器建立连接，在初始化阶段可以协商协议版本并交换各自支持的能力（Capabilities），比如确认服务器是否支持工具（Tools）功能。</p>
<p>比如下面的示例 POST Body，<code>protocolVersion</code> 字段表明了客户端支持的协议版本。</p>
<pre><code class="language-json">{
  &quot;method&quot;: &quot;initialize&quot;,
  &quot;params&quot;: {
    &quot;protocolVersion&quot;: &quot;2025-06-18&quot;,
    &quot;capabilities&quot;: {},
    &quot;clientInfo&quot;: {
      &quot;name&quot;: &quot;Cherry Studio&quot;,
      &quot;version&quot;: &quot;1.5.9&quot;
    }
  },
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 0
}
</code></pre>
<p>服务端示例 Response:</p>
<pre><code class="language-json">{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 0,
  &quot;result&quot;: {
    &quot;protocolVersion&quot;: &quot;2025-06-18&quot;,
    &quot;capabilities&quot;: {
      &quot;completions&quot;: {},
      &quot;prompts&quot;: {
        &quot;listChanged&quot;: false
      },
      &quot;resources&quot;: {
        &quot;subscribe&quot;: false,
        &quot;listChanged&quot;: false
      },
      &quot;tools&quot;: {
        &quot;listChanged&quot;: false
      }
    },
    &quot;serverInfo&quot;: {
      &quot;name&quot;: &quot;mcp-server&quot;,
      &quot;version&quot;: &quot;1.0.0&quot;
    }
  }
}
</code></pre>
<p>这个示例中，服务端也说明了自身支持的协议版本为 <code>2025-06-18</code>，同时从 <code>capabilities-&gt;tools</code> 中可以确定服务端支持 tools。</p>
<h3 id="通知-notificationsinitialized">通知 notifications/initialized</h3>
<p>在客户端发起 <code>initialize</code> 请求并收到服务端响应后，向服务端发送 <code>notifications/initialized</code> 用于通知服务器初始化已完成，可开始后续通信。</p>
<p>通知行为可以没有 <code>id</code> 字段。</p>
<pre><code class="language-json">{
    &quot;method&quot;: &quot;notifications/initialized&quot;,
    &quot;jsonrpc&quot;: &quot;2.0&quot;
}
</code></pre>
<p>服务端收到后响应无正文的 202 Accepted。</p>
<h3 id="ping">ping</h3>
<p>作用：用于检测连接是否依然可用，如客户端向服务端发起检测，反之亦然。</p>
<p>示例 POST Body :</p>
<pre><code class="language-json">{
    &quot;method&quot;: &quot;ping&quot;,
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;id&quot;: 1
}
</code></pre>
<p>服务端响应：</p>
<pre><code class="language-json">{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 1,
  &quot;result&quot;: {}
}
</code></pre>
<h3 id="toolslist">tools/list</h3>
<p>用于工具发现。在初始化完成后，客户端通过调用此方法获取服务器提供的所有工具及其详细定义，包括工具名称、描述以及遵循 JSON Schema 的输入参数模式（inputSchema）。
用途：用于获取支持的工具信息，以便于后续的 LLM 选择调用。
注意：若客户端已缓存工具信息且服务器未声明 <code>listChanged</code>，可跳过。</p>
<p>示例 POST Body :</p>
<pre><code class="language-json">{
    &quot;method&quot;: &quot;tools/list&quot;,
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;id&quot;: 2
}
</code></pre>
<p>响应</p>
<pre><code class="language-json">{
    &quot;id&quot;: 2,
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;result&quot;: {
        &quot;tools&quot;: [
            {
                &quot;description&quot;: &quot;获取城市天气&quot;,
                &quot;inputSchema&quot;: {
                    &quot;type&quot;: &quot;object&quot;,
                    &quot;properties&quot;: {
                        &quot;city&quot;: {
                            &quot;type&quot;: &quot;string&quot;
                        }
                    },
                    &quot;required&quot;: [
                        &quot;city&quot;
                    ],
                    &quot;additionalProperties&quot;: false
                },
                &quot;name&quot;: &quot;getWeather&quot;
            }
        ]
    }
}
</code></pre>
<h3 id="toolscall">tools/call</h3>
<p>这是工具调用的核心方法。客户端通过此方法发起工具调用，其中包含要调用的工具名称和具体参数（arguments），服务器执行对应操作后返回结果。</p>
<p>用途：客户端调用具体的服务端工具。</p>
<p>示例 POST Body :</p>
<pre><code class="language-json">{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 3,
  &quot;method&quot;: &quot;tools/call&quot;,
  &quot;params&quot;: {
    &quot;name&quot;: &quot;getWeather&quot;,
    &quot;arguments&quot;: { &quot;city&quot;: &quot;杭州&quot; }
  }
}
</code></pre>
<p>服务端响应：</p>
<pre><code class="language-json">{
    &quot;id&quot;: 3,
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;result&quot;: {
        &quot;content&quot;: [
            {
                &quot;type&quot;: &quot;text&quot;,
                &quot;text&quot;: &quot;杭州今日晴转多云&quot;
            }
        ],
        &quot;isError&quot;: false
    }
}
</code></pre>
<p>若工具内部出错，服务器应在响应中设置 <code>isError: true</code>，而非返回 JSON-RPC 错误，以便 LLM 能感知并自我修正 。</p>
<h2 id="手动实现-mcp-streamable-http-服务">手动实现 MCP Streamable HTTP 服务</h2>
<p>基于上述内容，实现一个基本的 MCP Streamable HTTP 服务，只需要对 MCP 生命周期各个阶段的 RPC 消息，进行解析处理并给出相应的响应即可。</p>
<p>为了简化实现，只需要支持 MCP 协议的关键生命周期方法：</p>
<ul>
<li><code>initialize</code>：协商协议版本并返回服务能力（声明支持 <code>tools</code>）。</li>
<li><code>notifications/initialized</code>：接收客户端初始化完成通知，返回 <code>202 Accepted</code>。</li>
<li><code>ping</code>：用于连接健康检查。</li>
<li><code>tools/list</code>：返回可用工具列表及其 JSON Schema 输入规范。</li>
<li><code>tools/call</code>：执行具体工具调用（如 <code>getWeather</code>）。</li>
</ul>
<h3 id="手动实现">手动实现</h3>
<p>一个基于 <strong>Spring Boot + Fastjson2</strong> 的 <strong>简易 MCP（Model Context Protocol）服务</strong>，提供 <strong>城市天气查询功能</strong>的实现如下。</p>
<ol>
<li>
<p>引入 Spring Boot WebMVC 和 FastJSON。</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-webmvc&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;com.alibaba.fastjson2&lt;/groupId&gt;
    &lt;artifactId&gt;fastjson2&lt;/artifactId&gt;
    &lt;version&gt;2.0.53&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
</li>
<li>
<p>定义接口 <code>/mcp</code> ，实现 MCP 生命周期的基本 RPC 消息的解析处理。</p>
<pre><code class="language-java">@RestController
@RequestMapping(&quot;/mcp&quot;)
public class McpWeatherController {

    private static final Logger log = LoggerFactory.getLogger(McpWeatherController.class);

    // 1. 静态化工具定义，使 tools/list 极其简洁
    private static final List&lt;Tool&gt; AVAILABLE_TOOLS = List.of(
        new Tool(&quot;getWeather&quot;, &quot;获取指定城市的天气预报&quot;,
            JSONObject.parseObject(&quot;&quot;&quot;
                {
                    &quot;type&quot;: &quot;object&quot;,
                    &quot;properties&quot;: { &quot;city&quot;: { &quot;type&quot;: &quot;string&quot;, &quot;description&quot;: &quot;城市名&quot; } },
                    &quot;required&quot;: [&quot;city&quot;],
                    &quot;additionalProperties&quot;: false
                }
                &quot;&quot;&quot;))
    );

    @PostMapping(consumes = &quot;application/json&quot;, produces = &quot;application/json&quot;)
    public ResponseEntity&lt;?&gt; handleMcpRequest(@RequestBody JsonRpcRequest request) {
        Object id = request.id();

         var response = switch (request.method()) {
            case &quot;initialize&quot; -&gt; ok(id, new InitializeResult());
            case &quot;notifications/initialized&quot; -&gt; accepted();
            case &quot;ping&quot; -&gt; ok(id, Map.of());
            case &quot;tools/list&quot; -&gt; ok(id, Map.of(&quot;tools&quot;, AVAILABLE_TOOLS));
            case &quot;tools/call&quot; -&gt; handleToolCall(id, request.params());
            default -&gt; ResponseEntity.notFound().build();
        };
        log.info(&quot;\nrequest: {}\nresponse:{}&quot;, JSON.toJSONString(request, Feature.PrettyFormat), JSON.toJSONString(response.getBody(),Feature.PrettyFormat));
         return response;
    }

    /**
     * 优雅处理工具调用：直接通过 JSONObject 转换，无需 String 二次中转
     */
    private ResponseEntity&lt;?&gt; handleToolCall(Object id, JSONObject params) {
        if (params == null) return badRequest();

        var callParams = params.toJavaObject(ToolCallParams.class);

        // 使用 switch 处理多工具扩展性更好
        return switch (callParams.name()) {
            case &quot;getWeather&quot; -&gt; {
                String city = String.valueOf(callParams.arguments().getOrDefault(&quot;city&quot;, &quot;未知城市&quot;));
                yield ok(id, new ToolCallResult(city + &quot;今日雷暴雨，建议居家&quot;));
            }
            default -&gt; badRequest();
        };
    }

    // --- 辅助方法 ---
    private static ResponseEntity&lt;JsonRpcResponse&gt; ok(Object id, Object result) {
        return ResponseEntity.ok(new JsonRpcResponse(id, result));
    }

    private static ResponseEntity&lt;Void&gt; accepted() {
        return ResponseEntity.status(202).build();
    }

    private static ResponseEntity&lt;Void&gt; badRequest() {
        return ResponseEntity.badRequest().build();
    }

    // --- MCP 协议 Records (Java 21) ---

    // 将 params 定义为 JSONObject，方便后续 toJavaObject 转换
    public record JsonRpcRequest(String jsonrpc, Object id, String method, JSONObject params) {}

    public record JsonRpcResponse(String jsonrpc, Object id, Object result) {
        public JsonRpcResponse(Object id, Object result) {
            this(&quot;2.0&quot;, id, result);
        }
    }

    // 初始化结果模型
    public record InitializeResult(String protocolVersion, Capabilities capabilities, ServerInfo serverInfo) {
        public InitializeResult() {
            this(&quot;2025-06-18&quot;, new Capabilities(new Tools(false)), new ServerInfo(&quot;mcp-weather-server&quot;, &quot;1.0.0&quot;));
        }
    }

    public record ServerInfo(String name, String version) {}
    public record Capabilities(Tools tools) {}
    public record Tools(boolean listChanged) {}

    // 工具定义模型
    public record Tool(String name, String description, Object inputSchema) {}

    // 工具调用参数模型
    public record ToolCallParams(String name, Map&lt;String, Object&gt; arguments) {}

    // 响应内容模型
    public record Content(String type, String text) {
        public Content(String text) { this(&quot;text&quot;, text); }
    }

    public record ToolCallResult(List&lt;Content&gt; content, boolean isError) {
        public ToolCallResult(String text) {
            this(List.of(new Content(text)), false);
        }
    }
}
</code></pre>
</li>
</ol>
<p>这就是完整代码了，启动后是一个最小化实现的 MCP Streamable HTTP 服务，它遵循 MCP Streamable HTTP 协议规范，利用 <strong>Java 21 的现代语言特性</strong>（如 <code>record</code>、<code>switch</code> 表达式、文本块等）来提升代码的简洁性与可读性。</p>
<h3 id="使用测试">使用测试</h3>
<p>我使用 LLM 客户端 Cherry Studio 添加这个 MCP 服务，然后询问北京天气，从日志中可以看到服务端在初始化-&gt;通知-&gt;<code>tools/list</code> 之后，进行了 <code>tools/call</code> 调用，并响应了“北京今日雷暴雨，建议居家”。</p>
<pre><code>request: {&quot;id&quot;:0,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;initialize&quot;,&quot;params&quot;:{&quot;protocolVersion&quot;:&quot;2025-06-18&quot;,&quot;capabilities&quot;:{},&quot;clientInfo&quot;:{&quot;name&quot;:&quot;Cherry Studio&quot;,&quot;version&quot;:&quot;1.5.9&quot;}}}
response: {&quot;id&quot;:0,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;result&quot;:{&quot;capabilities&quot;:{&quot;tools&quot;:{&quot;listChanged&quot;:false}},&quot;protocolVersion&quot;:&quot;2025-06-18&quot;,&quot;serverInfo&quot;:{&quot;name&quot;:&quot;mcp-weather-server&quot;,&quot;version&quot;:&quot;1.0.0&quot;}}}
-------------------
request: {&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;notifications/initialized&quot;}
response: null
-------------------
request: {&quot;id&quot;:1,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;tools/list&quot;}
response: {&quot;id&quot;:1,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;result&quot;:{&quot;tools&quot;:[{&quot;description&quot;:&quot;获取指定城市的天气预报&quot;,&quot;inputSchema&quot;:{&quot;type&quot;:&quot;object&quot;,&quot;properties&quot;:{&quot;city&quot;:{&quot;type&quot;:&quot;string&quot;,&quot;description&quot;:&quot;城市名&quot;}},&quot;required&quot;:[&quot;city&quot;],&quot;additionalProperties&quot;:false},&quot;name&quot;:&quot;getWeather&quot;}]}}
-------------------
request: {&quot;id&quot;:2,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;ping&quot;}
response: {&quot;id&quot;:2,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;result&quot;:{}}
-------------------
request: {&quot;id&quot;:3,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;prompts/list&quot;}
response: null
-------------------
request: {&quot;id&quot;:4,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;ping&quot;}
response: {&quot;id&quot;:4,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;result&quot;:{}}
-------------------
request: {&quot;id&quot;:5,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;resources/list&quot;}
response: null
-------------------
request: {&quot;id&quot;:6,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;ping&quot;}
response: {&quot;id&quot;:6,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;result&quot;:{}}
-------------------
request: {&quot;id&quot;:7,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;tools/call&quot;,&quot;params&quot;:{&quot;name&quot;:&quot;getWeather&quot;,&quot;arguments&quot;:{&quot;city&quot;:&quot;北京&quot;},&quot;_meta&quot;:{&quot;progressToken&quot;:9}}}
response: {&quot;id&quot;:7,&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;result&quot;:{&quot;content&quot;:[{&quot;text&quot;:&quot;北京今日雷暴雨，建议居家&quot;,&quot;type&quot;:&quot;text&quot;}],&quot;isError&quot;:false}}
</code></pre>
<p>文中代码已经上传到 <a href="https://github.com/niumoo/JavaNotes/tree/master/spring-ai/spring-mcp-server-manual?ref=wdbyte.com" target="_blank">Github.com/niumoo/JavaNotes</a> 仓库。</p>
<h2 id="参考">参考</h2>
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/transports?ref=wdbyte.com" target="_blank">https://modelcontextprotocol.io/specification/2025-06-18/basic/transports</a>
<a href="https://modelcontextprotocol.info/specification/draft/server/tools/?ref=wdbyte.com" target="_blank">https://modelcontextprotocol.info/specification/draft/server/tools/</a>
<a href="https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts?ref=wdbyte.com" target="_blank">https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts</a></p>
]]></content>
    </entry>
    <entry>
        <title>Project Valhalla&#xff1a;Java &#x7684;&#x6027;&#x80fd;&#x8dc3;&#x8fc1;</title>
        <link rel="alternate" type="text/html" href="https://www.wdbyte.com/project-valhalla-java/"/>
        <id>tag:www.wdbyte.com,/project-valhalla-java/</id>
        <published>2026-01-16T00:00:00Z</published>
        <updated>2026-01-16T00:00:00Z</updated>
        <summary>Valhalla &#x65e8;&#x5728;&#x5f15;&#x5165;&#x7528;&#x6237;&#x81ea;&#x5b9a;&#x4e49;&#x7684;&#x503c;&#x7c7b;&#x578b;&#xff08;value types&#xff09;&#xff0c;&#x4f7f;&#x5176;&#x884c;&#x4e3a;&#x5982;&#x540c;&#x57fa;&#x672c;&#x7c7b;&#x578b;&#x822c;&#x9ad8;&#x6548;&#xff0c;&#x540c;&#x65f6;&#x4fdd;&#x6301;&#x8868;&#x8fbe;&#x529b;&#x4e0e;&#x5b89;&#x5168;&#x6027;&#x3002;Valhalla &#x5c06;&#x5e26;&#x6765;&#x663e;&#x8457;&#x7684;&#x6027;&#x80fd;&#x63d0;&#x5347;&#xff0c;&#x5e76;&#x63a8;&#x52a8; Vector API &#x7b49;&#x9879;&#x76ee;&#x7684;&#x53d1;&#x5c55;&#xff0c;&#x786e;&#x4fdd; Java &#x5728;&#x4e0e; C&#x3001;Go &#x548c; Rust &#x7684;&#x7ade;&#x4e89;&#x4e2d;&#x59cb;&#x7ec8;&#x7acb;&#x4e8e;&#x4e0d;&#x8d25;&#x4e4b;&#x5730;&#x3002;</summary>
        <author>
            <name>程序猿阿朗</name>
            <uri>https://www.wdbyte.com</uri>
        </author>
        <content type="html" xml:lang="en" xml:base="https://www.wdbyte.com/"><![CDATA[为了更好的阅读体验，<a href="https://www.wdbyte.com/project-valhalla-java/">可以点击跳转到网页继续阅读.....</a></b><p>今天在看 Spring 的官方一篇文章时，看到下面一段描述。</p>
<blockquote>
<p>翻译后：这种思维对 Project Valhalla 至关重要。Valhalla 旨在引入用户自定义的值类型（value types），使其行为如同基本类型般高效，同时保持表达力与安全性。Valhalla 将带来显著的性能提升，并推动 Vector API 等项目的发展，确保 Java 在与 C、Go 和 Rust 的竞争中始终立于不败之地。</p>
</blockquote>
<p>出于好奇，我去检索学习了一下 <a href="https://openjdk.org/projects/valhalla/?ref=wdbyte.com" target="_blank">Project Valhalla</a> 的具体内容，看看是怎么做的，感觉很有收获，这篇文章因此而来。</p>
<h2 id="前言">前言</h2>
<p>作为 Java 开发者，你一定写过下面这样的代码。</p>
<pre><code class="language-java">List&lt;Integer&gt; numbers = new ArrayList&lt;&gt;();
numbers.add(42); // 自动装箱
int first = numbers.get(0); // 自动拆箱
</code></pre>
<p>我们已习以为常，这里 42 是 <code>int</code> 类型，但是 <code>ArrayList</code> 只能存储对象，所以使用 <code>Integer</code> 进行包装。在 Java 中这个过程叫<strong>装箱</strong>（boxing）。</p>
<p>那么问题来了，这里有多余性能损耗。</p>
<ul>
<li>每个 <code>Integer</code> 对象除了存 <code>42</code> 值外，还要额外占用 12~16 字节的对象头。</li>
<li>Integer 对象都分配在堆上，最后还得靠 GC 回收，增加 GC 压力。</li>
<li>ArrayList 内部 <code>Object[] data</code> 存储的元素都是引用地址，缓存命中率低，需要二次寻址。</li>
<li>如果要存一百万个整数，内存占用可能高达 <strong>28MB</strong>，而实际上只需要 <strong>4MB</strong>。</li>
</ul>
<p>那能不能直接写 <code>List&lt;int&gt;</code>？不行。Java 编译器会直接报错。这是 Java 从诞生起就有的一个“硬伤”：<strong>基本类型不能参与泛型</strong>。</p>
<p>怎么办呢？默认接受了吗？</p>
<h2 id="什么是-project-valhalla">什么是 Project Valhalla？</h2>
<p>示例代码中的问题，Oracle 和 OpenJDK 社区没有忘记，且已经给出解决方案在逐步实现。这个方案就是 Project Valhalla。</p>
<p>Valhalla 不是新语言，不是新语法，而是 Java 的一次真正的底层升级。它的目标很实在：<strong>让 Java 处理数据时更快、更省内存，同时代码还更好写</strong>。
简单说，Valhalla 要让 Java 支持一种新的“值类型”，让 <code>List&lt;int&gt;</code> 不仅合法，而且高效；让如 <code>Point[]</code> 之类的值对象数组可以真正连续存储，不再需要二次寻址，JVM 可能直接计算出 x 的值并返回，中间对象不需要了。</p>
<p>它主要干两件事：</p>
<ol>
<li><strong>引入值类型（Value Types）</strong>：你可以定义像 <code>Point</code>、<code>Money</code> 这样的类，它们在语义上是“值”（比如两个坐标相同的点就是同一个点），在性能上也如值类型 <code>int</code> 一样高效。</li>
<li><strong>实现泛型特化（Generic Specialization）</strong>：让 <code>List&lt;int&gt;</code> 成为可能，告别装箱拆箱，内部直接存原始数据。</li>
</ol>
<p>这些都不是语法糖，**Valhalla 会修改 JVM 的字节码、内存布局、垃圾回收策略等，**可以带来大幅度的性能提升。</p>
<h3 id="为什么叫-valhalla">为什么叫 “Valhalla”？</h3>
<p>名字来自北欧神话中的“英灵殿”——英雄死后去的地方。OpenJDK 团队喜欢用神话命名大项目（比如 Loom、Panama），Valhalla 代表“让 Java 性能登峰造极”的愿景。</p>
<h2 id="为什么-java-以前不这样做">为什么 Java 以前不这样做？</h2>
<h3 id="历史原因基本类型和对象的割裂">历史原因：基本类型和对象的“割裂”</h3>
<p>Java 1.0 为了简化内存管理，做了个妥协：</p>
<ul>
<li><code>int</code>、<code>double</code> 这些基本类型直接操作，速度快、无身份，但是不能继承。</li>
<li>其他一切（包括 <code>String</code>、<code>List</code>）都是引用对象，有身份、可继承，都是 <code>Object</code> 的子类，但处理起来速度会慢一点。</li>
</ul>
<p>问题从这里开始， JDK 1.2 增加了如 <code>ArrayList</code> 类时，使用 <code>Object</code> 类型引用对象， 到 Java 5 引入泛型时，问题出现了，为了兼容老代码，只能选择“类型擦除”——编译后所有泛型都变成 <code>Object</code>。这就导致：</p>
<pre><code class="language-java">List&lt;String&gt; list = new ArrayList&lt;&gt;();
// 编译后其实还是 List&lt;Object&gt;
</code></pre>
<p>所以 <code>List&lt;int&gt;</code> 根本不可能，因为 <code>int</code> 不是 <code>Object</code> 的子类，只能使用包装类 <code>Integer</code> 代替，这里就不可避免的需要装箱拆箱。</p>
<h3 id="性能代价装箱gc缓存失效">性能代价：装箱、GC、缓存失效</h3>
<p>装箱带来的问题不只是多几行字节码：</p>
<ul>
<li><strong>内存膨胀</strong>：一个 <code>int</code> 占 4 字节，但<code>Integer</code> 至少占 16 字节；</li>
<li><strong>GC 压力大</strong>：大量临时 <code>Integer</code> 对象让 GC 更忙；</li>
<li><strong>缓存不友好</strong>：数组里存的是指针引用，实际对象散落在堆各处，CPU 缓存经常失效。</li>
</ul>
<p>很多高性能场景（比如游戏、金融）不得不自己写 <code>IntList</code>、<code>DoubleArray</code> 来绕过这个问题。这不仅很麻烦，也破坏了代码统一性。</p>
<p>终于，JVM 发展这么多年，社区达成了共识：是时候解决这个老问题了。</p>
<h2 id="valhalla-到底在做什么">Valhalla 到底在做什么？</h2>
<p>你可能有点好奇了，Valhalla 怎么做才能解决面对这些问题。</p>
<h3 id="值类型没有身份的类">值类型：没有“身份”的类</h3>
<p>Valhalla 引入了一种新语法值类型（目前还在预览阶段），它通过关键词 <code>value class</code> 定义。</p>
<pre><code class="language-java">value class Point {
    int x;
    int y;
    
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
</code></pre>
<p>这里的关键点：</p>
<ul>
<li>值类型没有“对象身份”——两个 <code>x=1, y=2</code> 的 <code>Point</code> 实例，在逻辑上完全等同，</li>
<li>值类型没有“对象身份”，为了确保语义的一致性和 JVM 优化的可行性，字段必须是 <code>final</code>（不可变）的；</li>
<li>JVM 可以选择不把它当成普通对象，而是直接内联到其他对象或数组中。</li>
</ul>
<p>这意味着你可以安全地用它表示坐标、颜色、金额等“纯数据”类型，不用担心性能问题。</p>
<h3 id="泛型特化list-成为可能"><strong>泛型特化</strong>，List<int> 成为可能</h3>
<h3 id="id"></h3>
<p>Valhalla 的目标之一是支持像 <code>List&lt;int&gt;</code> 这样的语法，让泛型可以直接使用基本类型或值类型，从而避免装箱。但目前这一功能（称为‘泛型特化’）还在开发中，尚未在预览版 JDK 中可用。当前阶段，Valhalla 主要聚焦于值类型的定义与优化。</p>
<p>在泛型特化实现后，JVM 和标准库可能会为 <code>List&lt;int&gt;</code> 提供一个特化的实现，其内部使用 <code>int[]</code> 存储数据，从而避免装箱和间接寻址。这需要对集合类进行深度改造，并非简单语法糖。</p>
<p>或许怎么写还不知道，但可以肯定的是，可以没有装箱、拆箱。</p>
<p>同样，对于值类型：</p>
<pre><code class="language-java">List&lt;Point&gt; points = new ArrayList&lt;&gt;();
points.add(new Point(10, 20));
</code></pre>
<p>在内存中，<code>points</code> 的底层可能是一个连续的 <code>[10, 20, ...]</code> 数组，而不是 <code>[ref0, ref1, ...]</code> + 堆上分散的对象。</p>
<h3 id="内存布局改造从指针跳转到连续存储">内存布局改造：从“指针跳转”到“连续存储”</h3>
<p>当前 Java 中，<code>Point[]</code> 的内存布局是这样的：</p>
<pre><code>数组: [ptr0] → 堆上: [x=10, y=20]
      [ptr1] → 堆上: [x=30, y=40]
      [ptr2] → 堆上: [x=50, y=60]
</code></pre>
<p>遍历时，CPU 要先读指针，再跳到堆上读数据，缓存经常失效。</p>
<p>Valhalla 之后，变成：</p>
<pre><code>数组: [10, 20, 30, 40, 50, 60, ...]
</code></pre>
<p>连续内存 + 空间局部性 = <strong>缓存命中率大幅提升</strong>。在循环密集的场景（比如物理引擎每帧更新上千个粒子），性能提升将会非常明显。</p>
<h2 id="性能提升不止于集合">性能提升不止于集合</h2>
<p>看了上面介绍，好像性能提升只体现在 <code>ArrayList&lt;Integer&gt;</code> 这类集合上，其实不是的。</p>
<h3 id="对象字段也能扁平化">对象字段也能“扁平化”</h3>
<p>不光是数组，普通对象里的值类型字段也会被内联：</p>
<pre><code class="language-java">class Line {
    Point start; // 不再是指针，而是直接嵌入 x, y
    Point end;
}
</code></pre>
<p>内存布局从指针引用：</p>
<pre><code>Line → [start_ref, end_ref] → [x,y] + [x,y]
</code></pre>
<p>变成：</p>
<pre><code>Line → [x1, y1, x2, y2]
</code></pre>
<p>访问 <code>start.x</code> 和 <code>end.x</code> 可能在同一 CPU 缓存行，速度更快。</p>
<h3 id="方法调用更轻量">方法调用更轻量</h3>
<p>小的值类型（比如 <code>Complex</code> 复数）可以完全通过 CPU 寄存器传递，不需要读写内存。JIT 编译器甚至能优化掉整个临时对象：</p>
<pre><code class="language-java">Point mid = midpoint(p1, p2); // 返回的 Point 可能根本不分配内存
return mid.x;
</code></pre>
<p>在这种情况下，JVM 可能直接计算出 <code>x</code> 的值并返回，中间对象“消失”了。</p>
<h3 id="为未来的硬件优化铺路">为未来的硬件优化铺路</h3>
<p>连续的值类型数组更容易被 JVM 自动向量化（SIMD）。比如，HotSpot 未来可能用一条 AVX 指令同时处理 4 个 <code>x</code> 坐标。这在图像处理、机器学习推理中非常有用。</p>
<p>实测数据显示，在粒子模拟、金融定价等场景中，Valhalla 原型已实现 <strong>2~5 倍的吞吐提升</strong>，内存占用减少 <strong>70% 以上</strong>。</p>
<h2 id="对开发者意味着什么">对开发者意味着什么？</h2>
<h3 id="直接的性能提升">直接的性能提升</h3>
<p>JDK 非常重视向后兼容，因此老版本代码完全不受影响。JVM 也会在内部优化某些模式，什么都不修改也可带来性能提升。新代码可以逐步采用值类型。</p>
<h3 id="代码更自然">代码更自然</h3>
<p>手段多了，不用再纠结“用 Class 还是用 record 还是自己写”，以前你要表示一个坐标，可能会犹豫：</p>
<ul>
<li>用 <code>record Point(int x, int y)</code>？但它是个普通对象，有身份，存进 <code>List</code> 需要指针引用。</li>
<li>自己实现如 <code>IntList</code>类？又怕性能不好。</li>
</ul>
<p>有了 Valhalla 后，<code>value class</code> 就是为这类场景设计的：<strong>语义上是值，性能上也是值</strong>。和 <code>record</code>两者互补，覆盖不同场景。</p>
<h3 id="jdk-原生支持不需要第三方集合库">JDK 原生支持，不需要第三方集合库</h3>
<p>当前为了高效存 <code>int</code>，你可能要引用其他集合库，然后使用如 <code>IntList</code> 类：</p>
<pre><code class="language-java">// 当前做法
IntList list = new IntArrayList();
list.add(42);
</code></pre>
<p>Valhalla 后，标准 <code>ArrayList</code> 就够了：</p>
<pre><code class="language-java">// 未来做法
List&lt;int&gt; list = new ArrayList&lt;&gt;();
list.add(42);
</code></pre>
<p>生态统一，学习成本更低。</p>
<h2 id="valhalla-什么时候能用">Valhalla 什么时候能用？</h2>
<p>目前 <code>value class</code> 已进入 Preview 阶段。如果你想体验，可以在 <a href="https://jdk.java.net/valhalla/?ref=wdbyte.com" target="_blank">jdk.java.net/valhalla</a> 下载抢先体验的JDK 版本。</p>
<p>官方为此写了一篇文章：<a href="https://inside.java/2025/10/27/try-jep-401-value-classes/?ref=wdbyte.com" target="_blank">Try Out JEP 401 Value Classes and Objects</a></p>
<p>从文章中可以看到，仅“创建一个非常大的 <code>LocalDate</code> 值对象数组，并将它们的年份值相加” 这样的例子，在降低内存占用的同时，还带来 3 倍左右的速度提升。</p>
<h2 id="总结">总结</h2>
<p>Project Valhalla 是为了解决 Java 真实存在的痛点：<strong>不能高效地处理一堆数字。是真正的底层革新。</strong></p>
<p>它通过值类型和泛型特化，让 Java 在保留简单安全优势的同时，补齐了高性能数据处理的短板。从表面上看，它可能只是让 <code>List&lt;int&gt;</code> 变得符合语法，但对那些需要榨干每一滴性能的场景，它可能是 Java 能否继续胜任的关键。</p>
<p>最后想说。初学 Java 时，我们可能疑惑：“我想把 <code>int</code> 直接放进 <code>List</code>,为什么不行”？但是在知道装箱拆箱之后，就不再追问了，没想到这个问题社区一直在努力解决，而现在，快实现了。</p>
<h2 id="参考">参考</h2>
<ol>
<li><a href="https://openjdk.org/projects/valhalla/?ref=wdbyte.com" target="_blank">Project Valhalla</a></li>
<li><a href="https://inside.java/2025/10/27/try-jep-401-value-classes/?ref=wdbyte.com" target="_blank">Try Out JEP 401 Value Classes and Objects</a></li>
<li><a href="https://jdk.java.net/valhalla/?ref=wdbyte.com" target="_blank">Project Valhalla Early-Access Builds</a></li>
<li><a href="https://openjdk.org/jeps/8277163?ref=wdbyte.com" target="_blank">JEP draft: Value Objects (Preview)</a></li>
<li><a href="https://openjdk.org/jeps/401?ref=wdbyte.com" target="_blank">JEP 401: Value Class And Objects</a></li>
</ol>
]]></content>
    </entry>
    <entry>
        <title>Spring &#x5e74;&#x5ea6;&#x56de;&#x987e; -2025</title>
        <link rel="alternate" type="text/html" href="https://www.wdbyte.com/spring-2025/"/>
        <id>tag:www.wdbyte.com,/spring-2025/</id>
        <published>2026-01-13T00:00:00Z</published>
        <updated>2026-01-13T00:00:00Z</updated>
        <summary>&#x8fd9;&#x7bc7;&#x6587;&#x7ae0;&#x603b;&#x7ed3;&#x4e86; Spring &#x5728; 2025 &#x5e74;&#x7684;&#x4e00;&#x4e9b;&#x4eae;&#x70b9;&#x5de5;&#x4f5c;&#xff0c;&#x6211;&#x60f3;&#xff0c;&#x4e86;&#x89e3;&#x8fd9;&#x4e9b;&#x5bf9;&#x6211;&#x4eec;&#x5f00;&#x53d1;&#x8005;&#x6765;&#x8bb2;&#x5f88;&#x6709;&#x7528;&#x5904;&#x3002;</summary>
        <author>
            <name>程序猿阿朗</name>
            <uri>https://www.wdbyte.com</uri>
        </author>
        <content type="html" xml:lang="en" xml:base="https://www.wdbyte.com/"><![CDATA[为了更好的阅读体验，<a href="https://www.wdbyte.com/spring-2025/">可以点击跳转到网页继续阅读.....</a></b><blockquote>
<p>这篇文章总结了 Spring 在 2025 年的一些亮点工作，我想，了解这些对我们开发者来讲很有用处，本文翻译自 <a href="https://spring.io/blog/2025/12/30/this-year-in-spring-december-30th-2025/?ref=wdbyte.com" target="_blank">Spring 官方博客</a>。</p>
</blockquote>
<p>大家好，Spring 粉丝们！你们敢信吗？今天已经是 12 月 30 日了！我在洛杉矶和家人一起庆祝了圣诞节，随后我们立刻飞往东南亚，准备与更多亲朋好友一同迎接新年的到来。此刻，我正坐在马来西亚吉隆坡这座炎热潮湿的城市中的一家咖啡馆里写下这些文字——满怀温情地回顾 2025 年的最后一周，以及整个 2025 年。</p>
<p>没错！又到了我一年一度的传统环节：回溯式年终盘点与深度分析。欢迎来到《This Year in Spring》，在这里我们将回顾过去一年中那些美好、卓越乃至惊艳的五大亮点。（当然，我们也会照常带来本周的新闻速递。）</p>
<p>那么，首先让我们来看看 2026 年的五大核心主题。</p>
<h2 id="spring-boot-4"><strong>Spring Boot 4</strong></h2>
<p>这一点你肯定早有预料。2025 年是 Spring 生态系统翻开全新篇章的一年：从 Spring Framework 7 开始，包括 Spring Security 7、Spring Batch 6 在内的众多 Spring 项目纷纷跟进升级。紧接着，我们迎来了 Spring Boot 4！（顺便说一句，现在其实已经发布到 Spring Boot 4.0.1 了……）</p>
<p>Spring Boot 4 集成了大量令人惊叹的新特性，不仅整合了整个生态系统的改进成果，还在 Spring Boot 自身提供了诸多便利功能。亮点包括声明式接口客户端、API 版本控制、统一的 Spring Security 支持、全新的无资源（resourceless）Spring Batch 以及基于 MongoDB 的仓库实现、名为 BeanRegistrar 的新配置模型，等等。</p>
<p>在此版本基础上，还涌现出一批所谓的“post-Boot”项目——它们以 Spring Boot 本身作为依赖项。这其中包括 Spring Cloud、Spring Modulith、Spring gRPC（目前阶段）以及 Spring AI。除 Spring AI 外，其他项目均已发布了兼容 Spring Boot 4 的 GA 版本；而 Spring AI 2.0 也已提供里程碑版本供试用。</p>
<p>这一新版 Spring Boot 代表了一个具备深远新特性的全新世代。和往常一样，你可以在 <a href="https://start.spring.io/?ref=wdbyte.com" target="_blank">Spring Initializr</a> 上立即尝试！</p>
<h2 id="ainext"><strong>AI.next</strong></h2>
<p>2025 年，通过我在 YouTube、播客以及无数场大会演讲中的分享，我向数百万人介绍了令人震撼的 Spring AI 项目。它在 JVM 平台上提供了最丰富的 AI 技术集成能力。</p>
<p>早在 2024 年 11 月（距今已超过一年），我们就启动了对 MCP 规范的集成工作——当时 Anthropic 刚刚提出这一初步构想。进入 2025 年初，我们将这项工作捐赠给了 MCP 项目本身，并成为其官方 Java SDK 的核心开发团队。随后，我们将 Spring AI 的集成层重构至这一全新基础 SDK 之上。此后，我们持续快速推进开发，紧跟规范的每一次迭代，同时不断完善组件模型等关键部分。MCP 是一项具有划时代意义的技术，将催生一个完整的集成生态和无限机遇。你会用它构建出什么？</p>
<p>你上一次与 AI 大语言模型（LLM）对话时，是否曾觉得第一次尝试就得到了完美回答？我敢打赌，几乎从未有过。这种现象在成本更低、规模更小但实用性极强的模型上尤为明显——它们缺乏前沿大模型那种“读懂你心思”的能力。不过，你可以通过一系列明确定义的步骤累积状态，从而获得更优结果——这正是诸如 Embabel 这类“智能体框架”（agentic frameworks）所专注的领域，而 Embabel 正是构建于 Spring AI 之上。</p>
<p>说到 Spring AI，它的发展速度简直惊人。虽然它本身已内置了 20 多种模型集成和 20 多种向量存储集成，但并非所有内容都能纳入核心项目。因此，我们创建了 <a href="https://github.com/spring-ai-community?ref=wdbyte.com" target="_blank">Spring AI Community </a>组织，在那里你可以找到更多模型与集成方案。</p>
<p>以下三个项目绝对值得关注：</p>
<ul>
<li><a href="https://github.com/spring-ai-community/spring-ai-agents?ref=wdbyte.com" target="_blank"><strong>Spring AI Agents</strong></a></li>
<li><a href="https://github.com/spring-ai-community/mcp-security?ref=wdbyte.com" target="_blank"><strong>Spring MCP Security</strong></a></li>
<li><a href="https://github.com/spring-ai-community/spring-ai-bench?ref=wdbyte.com" target="_blank"><strong>Spring AI Bench</strong></a></li>
</ul>
<p>Spring AI Agents 项目支持将智能体 CLI（如 OpenAI Codex 和 Claude Code）封装为服务，同时还提供了对 ACP（Agent Client Protocol）的丰富封装。为什么要使用 CLI 而非直接调用模型？因为你的操作系统本身就提供了丰富的工具集，而 AI 能借助它们完成令人惊叹的任务。通过 Spring AI Agents，你可以绕过中间环节，或对其进行编排。</p>
<p>当前，AI 智能体 CLI 社区面临一个基准测试难题。事实上的标准是 SWE 基准，但它用 Python 编写，且任务内容已过时多年。坦白讲：模型早已“背熟”了这些题目，在该基准上表现极佳（准确率超 70%）。但一旦换成 Java 任务，得分便急剧下滑。Spring AI Bench 正致力于提供贴近现实、现代化的工作负载，确保测试可快速且可复现。</p>
<p>顾名思义，Spring MCP Security 会紧密跟踪 MCP 安全规范的变更，并为 Spring AI 与 Spring Security 用户提供深度集成。安全性、可观测性与可扩展性是企业级 AI 的三大基石——而在这些方面，无人能出 Spring 之右。</p>
<h2 id="security-as-a-feature安全即特性"><strong>Security as a Feature（安全即特性）</strong></h2>
<p>Spring Security 在 2025 年取得了巨大进展。近期发布的 Spring Security 6.x 引入了一次性令牌（one-time tokens）和 WebAuthn（即广为人知的“通行密钥”，passkeys）。通行密钥是一种极其出色、用户友好且高度安全的方式，可将生物识别与多因素认证无缝集成到应用中。</p>
<p>作为 Spring Boot 4 发布列车的一部分，Spring Security 7 更进一步，内置了多因素认证支持。它建立在已有的丰富集成基础之上，如 SAML、Kerberos 和 OAuth。顺便一提：Kerberos 支持以及独立的 Spring Authorization Server（一个完整的 OAuth 身份提供商）如今也已纳入 Spring Security 7！</p>
<h2 id="the-open-web开放的-web"><strong>The Open Web（开放的 Web）</strong></h2>
<p>Spring 最初就是一个 Web 框架。自 Spring Framework 诞生之初，Spring MVC 便已随行。不久之后，Spring-WS 于 2007 年推出，用于支持 SOAP 服务。2009 年，Spring Framework 3 加入了强大的 REST 服务 HTTP 支持，后续又通过 Spring HATEOAS 增强了超媒体能力。</p>
<p>此后，我们探索了二进制协议 RSocket，并与 GraphQL Java 团队合作推出了功能完备的 GraphQL 支持。最近，我们宣布了 Spring gRPC，提供一流的 gRPC 支持，涵盖安全、可观测性及 GraalVM 编译能力。Spring gRPC 已在 Spring Boot 4 发布后不久正式 GA——现在就去试试吧！</p>
<h2 id="a-java-for-today-and-tomorrow面向当下与未来的-java"><strong>A Java for Today and Tomorrow（面向当下与未来的 Java）</strong></h2>
<p>快速问答：下面这段代码是什么语言？</p>
<pre><code class="language-java">void main() {
  IO.println(&quot;hello, world!&quot;);
}
</code></pre>
<p>你猜对了：这是 Java！把它保存为 <code>script.java</code>，然后直接运行 <code>java script.java</code> 即可——无需编译步骤。你刚刚运行了史上第一个优秀的 Java 脚本（Java script）！世上有很多糟糕的 JavaScript，但这是一个 Java script，而且棒极了！注意，这里完全不需要编译？另外，你是否注意到代码中没有外层类？它和我一样，“毫无 class”（no class）！随着每个新版本的发布，Java 的开发者体验正变得越来越好！</p>
<p>如今的 Java 借助虚拟线程（virtual threads）实现了毫不费力的横向扩展，提供了类似 Go 语言的可扩展性，却避免了 async/await 的冗长语法。你只需在 Spring Initializr 上勾选 GraalVM，然后运行以下命令，即可将 Spring Boot 应用转为 GraalVM 原生镜像：</p>
<pre><code class="language-bash">./mvnw -Pnative native:compile
</code></pre>
<p>还不打算全面转向原生镜像？Project Leyden 和 AOT 缓存也能带来类似的启动加速效果——通常可将启动时间缩短 75% 以上。当然，我们也已内置了对这些技术的支持！</p>
<p>Brian Goetz 在其演讲《Growing the Java Language》中提出了一种极具说服力的理念：“可生长”特性（growable features）——即语法、类型与库协同演进。例如 <code>Iterable&lt;T&gt;</code> 和 <code>AutoCloseable</code>，这两个简单接口便解锁了强大的语言特性。</p>
<p>这种思维对 Project Valhalla 至关重要。Valhalla 旨在引入用户自定义的值类型（value types），使其行为如同基本类型般高效，同时保持表达力与安全性。Valhalla 将带来显著的性能提升，并推动 Vector API 等项目的发展，确保 Java 在与 C、Go 和 Rust 的竞争中始终立于不败之地。</p>
<p>对于 Java 和 Spring 开发者而言，当下正是最好的时代，而明年只会更加精彩！</p>
<p>那么，话不多说，让我们直接进入本周的新闻速递！</p>
<h2 id="the-roundup本周速递"><strong>The Roundup（本周速递）</strong></h2>
<ul>
<li><a href="https://spring.io/blog/2025/12/26/evolving-spring-vault?ref=wdbyte.com" target="_blank">Evolving Spring Vault: Introducing VaultClient</a></li>
<li>Christian Tsolov 分享：高效调用<a href="https://spring.io/blog/2025/12/23/spring-ai-tool-argument-augmenter-tzolov/?ref=wdbyte.com" target="_blank"> Spring AI 工具</a></li>
<li><a href="https://medium.com/@pmLearners/spring-boot-4-the-7-breaking-changes-every-developer-must-know-99de4c2b60e2?ref=wdbyte.com" target="_blank">Spring Boot 4：破坏性变更一览</a></li>
<li><a href="https://www.baeldung.com/orkes-conductor-guide?ref=wdbyte.com" target="_blank">使用 Orkes Conductor 编排工作流</a></li>
<li><a href="https://www.baeldung.com/faunadb-spring?ref=wdbyte.com" target="_blank">FaunaDB 与 Spring 集成</a></li>
<li>James Ward 提问：<a href="https://x.com/jamesward/status/2005742419594117291?s=12&amp;ref=wdbyte.com" target="_blank">2026 年会是智能体 AI（agentic AI）之年吗？</a></li>
<li><a href="https://itnext.io/first-class-kotlin-serialization-support-in-spring-boot-4-54a8e930c60b?ref=wdbyte.com" target="_blank">Spring Boot 4 中的 Kotlin 序列化</a></li>
<li><a href="https://blog.stackademic.com/spring-boot-temporal-io-why-orchestrators-are-the-future-of-java-2025-edition-a7c2d6ff8b48?ref=wdbyte.com" target="_blank">Spring Boot + Temporal.io</a></li>
<li><a href="https://www.baeldung.com/hikaricp?ref=wdbyte.com" target="_blank">HikariCP 入门指南</a></li>
<li><a href="https://x.com/intellijidea/status/2000566592384422334?s=12&amp;ref=wdbyte.com" target="_blank">IntelliJ IDEA 对 Spring Data JDBC 的支持</a></li>
<li><a href="https://www.baeldung.com/spring-data-cassandra-dates?ref=wdbyte.com" target="_blank">使用 Spring Data Cassandra 保存日期值</a></li>
<li><a href="https://blogs.vmware.com/tanzu/tanzu-platform-still-the-best-place-for-enterprise-java-apps/?ref=wdbyte.com" target="_blank">Tanzu Platform 仍是企业级 Java 应用的最佳归宿</a></li>
<li><a href="https://youtube.com/shorts/Gi3L9lVKUzw?si=lCEru6DD8q5wx60V&amp;ref=wdbyte.com" target="_blank">我最爱的企业级 starter：Spring Boot Governance Starter</a></li>
</ul>
<p>以上就是本期全部内容！这也是《This Week in Spring》在 2025 年的最后一期。下周将迎来 2026 年的第一期——想想真是不可思议，因为我从 2011 年第一周开始撰写这些文章，下周就将迎来《This Week in Spring》的第 15 个年头！（而且我从未间断过一周！）</p>
<p>我相信，我完全可以代表整个 Spring 团队，衷心祝愿你和你的家人新年平安、快乐、健康！</p>
]]></content>
    </entry>
</feed>