<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>设计模式 on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/</link><description>Recent content in 设计模式 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 27 Feb 2026 23:00:00 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/index.xml" rel="self" type="application/rss+xml"/><item><title>一文讲透 GoF 的 23 种设计模式之工厂方法</title><link>https://xiaobox.github.io/p/2026-02-27-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-gong-chan/</link><pubDate>Fri, 27 Feb 2026 23:00:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-02-27-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-gong-chan/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-27-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-gong-chan/cover.jpg" alt="Featured image of post 一文讲透 GoF 的 23 种设计模式之工厂方法" /&gt;&lt;h1 id="一文讲透-gof-的-23-种设计模式之工厂方法"&gt;&lt;a href="#%e4%b8%80%e6%96%87%e8%ae%b2%e9%80%8f-gof-%e7%9a%84-23-%e7%a7%8d%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e4%b9%8b%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;一文讲透 GoF 的 23 种设计模式之工厂方法
&lt;/h1&gt;&lt;p&gt;工厂方法（Factory Method） 是创建型模式&lt;/p&gt;
&lt;h2 id="定义"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;用一句话概括工厂方法模式：定义一个用于创建对象的接口，让子类决定实例化哪一个类。 它让类的实例化推迟到了子类。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-27-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-gong-chan/001-00e336ad.png"&gt;&lt;/p&gt;
&lt;h2 id="简单工厂"&gt;&lt;a href="#%e7%ae%80%e5%8d%95%e5%b7%a5%e5%8e%82" class="header-anchor"&gt;&lt;/a&gt;简单工厂
&lt;/h2&gt;&lt;p&gt;了解工厂方法模式前，我们先了解下简单工厂，既然叫简单工厂，那自然很 “简单”。&lt;/p&gt;
&lt;p&gt;它的核心思想非常直接：专门定义一个类（包揽大权），通过接收不同的参数，用 switch 或 if-else 来决定创建并返回哪一种具体的产品实例。&lt;/p&gt;
&lt;p&gt;假设我们在开发一个 AI 应用，需要根据不同场景创建不同类型的 AI Agent（比如负责对话的 Agent，和负责处理数据的 Agent）。&lt;/p&gt;
&lt;h3 id="第一步定义产品的共同接口和具体实现"&gt;&lt;a href="#%e7%ac%ac%e4%b8%80%e6%ad%a5%e5%ae%9a%e4%b9%89%e4%ba%a7%e5%93%81%e7%9a%84%e5%85%b1%e5%90%8c%e6%8e%a5%e5%8f%a3%e5%92%8c%e5%85%b7%e4%bd%93%e5%ae%9e%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;第一步：定义产品的共同接口和具体实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段&lt;/span&gt;&lt;span class="c1"&gt;// 1. 抽象产品&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;AIAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voidexecuteTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 2. 具体产品 A：聊天助理&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;publicclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ChatAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publicvoidexecuteTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ChatAgent: 正在与用户进行自然语言对话...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 2. 具体产品 B：数据分析助理&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;publicclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataAnalysisAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publicvoidexecuteTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;DataAnalysisAgent: 正在提取并分析核心数据...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第二步创建简单工厂类"&gt;&lt;a href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e5%88%9b%e5%bb%ba%e7%ae%80%e5%8d%95%e5%b7%a5%e5%8e%82%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;第二步：创建“简单工厂”类
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;⚡ java片段// 3. 简单工厂类 (通常使用静态方法)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;publicclass AIAgentFactory {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; // 根据传入的类型参数，决定实例化哪个具体的 Agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; publicstatic AIAgent createAgent(String agentType) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; if (&amp;#34;chat&amp;#34;.equalsIgnoreCase(agentType)) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; return new ChatAgent();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; } elseif (&amp;#34;data&amp;#34;.equalsIgnoreCase(agentType)) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; return new DataAnalysisAgent();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; } else {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; throw new IllegalArgumentException(&amp;#34;未知的 Agent 类型: &amp;#34; + agentType);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第三步客户端调用"&gt;&lt;a href="#%e7%ac%ac%e4%b8%89%e6%ad%a5%e5%ae%a2%e6%88%b7%e7%ab%af%e8%b0%83%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;第三步：客户端调用
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 客户端不需要知道 ChatAgent 和 DataAnalysisAgent 是怎么被 new 出来的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 只需要告诉工厂：“给我一个 chat 类型的 Agent”&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;agent1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AIAgentFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;agent1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;agent2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AIAgentFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;agent2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结合代码，我们可以很直观地看到它的特点：&lt;/p&gt;
&lt;p&gt;●优点（省事、解耦）：客户端彻底和具体的实现类解耦了。你不需要在业务代码里到处写 new ChatAgent()，把“创建对象”的脏活累活全交给了工厂。&lt;/p&gt;
&lt;p&gt;●缺点（牵一发而动全身）：它严重违反了“开闭原则”（对扩展开放，对修改关闭）。假设我们现在要引入一个新的 CodingAgent（写代码助手），除了要新建产品类，你必须去修改 AIAgentFactory 里面的 if-else 代码。一旦产品种类极其庞大，这个工厂类就会变得非常臃肿且难以维护。&lt;/p&gt;
&lt;p&gt;正是为了解决简单工厂“违反开闭原则”的这个致命缺点，才演进出了工厂方法模式（把这一个大工厂，拆成了一个个不用改代码、只需新增的具体小工厂）。&lt;/p&gt;
&lt;h2 id="工厂方法模式的结构与角色"&gt;&lt;a href="#%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95%e6%a8%a1%e5%bc%8f%e7%9a%84%e7%bb%93%e6%9e%84%e4%b8%8e%e8%a7%92%e8%89%b2" class="header-anchor"&gt;&lt;/a&gt;工厂方法模式的结构与角色
&lt;/h2&gt;&lt;p&gt;工厂方法模式主要包含四个角色：&lt;/p&gt;
&lt;p&gt;●抽象产品 (Product)：定义产品的统一接口。&lt;/p&gt;
&lt;p&gt;●具体产品 (Concrete Product)：实现抽象产品接口的具体类。&lt;/p&gt;
&lt;p&gt;●抽象工厂 (Creator)：声明返回产品对象的工厂方法。&lt;/p&gt;
&lt;p&gt;●具体工厂 (Concrete Creator)：重写工厂方法，返回具体的实例化产品&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-27-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-gong-chan/002-5fb65f39.png"&gt;&lt;/p&gt;
&lt;h2 id="java-代码实现"&gt;&lt;a href="#java-%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;Java 代码实现
&lt;/h2&gt;&lt;h3 id="1-定义产品大模型客户端"&gt;&lt;a href="#1-%e5%ae%9a%e4%b9%89%e4%ba%a7%e5%93%81%e5%a4%a7%e6%a8%a1%e5%9e%8b%e5%ae%a2%e6%88%b7%e7%ab%af" class="header-anchor"&gt;&lt;/a&gt;1. 定义产品（大模型客户端）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段&lt;/span&gt;&lt;span class="c1"&gt;// 抽象产品：统一的大模型调用接口&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 具体产品 A：Claude 客户端&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;publicclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClaudeClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publicClaudeClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelVersion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[Claude &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelVersion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;] 思考并返回结果...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 具体产品 B：OpenAI 客户端&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;publicclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OpenAIClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publicOpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[OpenAI API] 处理输入并返回结果...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-定义创建者核心业务骨架--工厂方法"&gt;&lt;a href="#2-%e5%ae%9a%e4%b9%89%e5%88%9b%e5%bb%ba%e8%80%85%e6%a0%b8%e5%bf%83%e4%b8%9a%e5%8a%a1%e9%aa%a8%e6%9e%b6--%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;2. 定义创建者（核心：业务骨架 + 工厂方法）
&lt;/h3&gt;&lt;p&gt;这里是关键：AgentWorkflow 不是一个纯粹的“工厂类”，它是业务类，工厂方法只是它的一部分。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段&lt;/span&gt;&lt;span class="c1"&gt;// 抽象创建者：Agent 工作流骨架&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 核心业务逻辑：定义了标准的处理流程（这其实也是个模板方法）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publicvoidprocessTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taskContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;=== 1. 解析任务上下文，提取关键信息 ===&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 【灵魂所在】：这里调用工厂方法，拿到一个产品对象。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 父类在此刻完全不知道自己拿到的是 Claude 还是 OpenAI。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;createLLMClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;=== 2. 请求大模型进行推理 ===&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taskContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;=== 3. 结果后处理并落库 ===\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 【工厂方法】：将实例化具体产品的职责，推迟到子类去实现&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createLLMClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-定义具体创建者子类重写工厂方法"&gt;&lt;a href="#3-%e5%ae%9a%e4%b9%89%e5%85%b7%e4%bd%93%e5%88%9b%e5%bb%ba%e8%80%85%e5%ad%90%e7%b1%bb%e9%87%8d%e5%86%99%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;3. 定义具体创建者（子类重写工厂方法）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段&lt;/span&gt;&lt;span class="c1"&gt;// 具体创建者 A：基于 Claude 的工作流&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;publicclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClaudeAgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createLLMClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这里封装 Claude 特有的复杂初始化逻辑（比如加载凭证、设置代理等）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; -&amp;gt; [工厂方法] 正在初始化 Claude 客户端环境...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClaudeClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;3.5-Sonnet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 具体创建者 B：基于 OpenAI 的工作流&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;publicclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OpenAIAgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LLMClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createLLMClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; -&amp;gt; [工厂方法] 正在构建 OpenAI 客户端环境...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://api.openai.com/v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="4-客户端调用"&gt;&lt;a href="#4-%e5%ae%a2%e6%88%b7%e7%ab%af%e8%b0%83%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;4. 客户端调用
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publicstaticvoidmain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;编写一段 Python Web 框架对比报告&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 场景 1：启动基于 Claude 的 Agent 工作流&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;claudeWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClaudeAgentWorkflow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;claudeWorkflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 场景 2：切换为基于 OpenAI 的 Agent 工作流&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AgentWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openaiWorkflow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OpenAIAgentWorkflow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openaiWorkflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你回看之前的例子，你会发现这个 Demo 解决了一个架构设计上的核心痛点：控制反转 (IoC) 的雏形。&lt;/p&gt;
&lt;p&gt;在 AgentWorkflow 这个父类中，业务主流程已经被彻底固化并复用（processTask 方法）。如果在未来，业务需求要求你接入一个全新的本地开源模型（比如 DeepSeek），你不需要修改任何现有的主流程代码，只需要：&lt;/p&gt;
&lt;p&gt;●新建一个 DeepSeekClient（实现 LLMClient）。&lt;/p&gt;
&lt;p&gt;●新建一个 DeepSeekAgentWorkflow，重写 createLLMClient() 方法返回这个新 Client。&lt;/p&gt;
&lt;p&gt;这才是工厂方法模式真正强大的地方：它是为了让高层模块（业务骨架）能够独立于底层模块（具体产品）的创建而存在，从而支撑起大型框架的扩展性。 JDK 里的 Iterable 接口和它的 iterator() 方法，本质上就是这种工厂方法模式的经典体现。&lt;/p&gt;
&lt;h2 id="什么时候用"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;什么时候用?
&lt;/h2&gt;&lt;p&gt;●你写的“父类流程”需要创建某种对象，但父类不该/不想知道具体类是谁（框架留扩展点的典型方式）。&lt;/p&gt;
&lt;p&gt;●你希望通过继承覆写来扩展“产物类型”，让调用方不动、流程不动。&lt;/p&gt;
&lt;p&gt;一些具体的场景：&lt;/p&gt;
&lt;p&gt;●框架扩展点：工厂方法很常见于“框架规定流程、业务方覆写创建”的场景（你写子类接入框架）。&lt;/p&gt;
&lt;p&gt;●Spring 的 FactoryBean：它的语义就是“这个 bean 不是普通 bean，而是用来生产另一个对象的”，并且暴露的是 getObject() 创建出来的对象。&lt;/p&gt;
&lt;p&gt;●Java ServiceLoader：通过 SPI 在运行时发现/加载实现类，属于“把具体实现延迟到运行时配置/部署”的一类机制，和“解耦创建与使用”的目标一致。&lt;/p&gt;
&lt;h2 id="注意模式的命名"&gt;&lt;a href="#%e6%b3%a8%e6%84%8f%e6%a8%a1%e5%bc%8f%e7%9a%84%e5%91%bd%e5%90%8d" class="header-anchor"&gt;&lt;/a&gt;注意模式的命名
&lt;/h2&gt;&lt;p&gt;我们回头看一下这个模式为什么叫 &lt;strong&gt;Factory Method&lt;/strong&gt;，而不是干脆叫 Factory ? 这个命名是有讲究的。&lt;/p&gt;
&lt;p&gt;核心原因在于：这个模式的灵魂是一个“方法”，而不是一个“类”。&lt;/p&gt;
&lt;p&gt;1.“工厂 (Factory)”是一个通俗的广义概念：&lt;/p&gt;
&lt;p&gt;在日常沟通中，只要一个类的主要职责是造对象，我们都叫它工厂（比如前面提过的“简单工厂”，它就是一个充斥着 if-else 的具体类）。&lt;/p&gt;
&lt;p&gt;2.“工厂方法 (Factory Method)”强调的是面向对象中的“多态”与“继承”：&lt;/p&gt;
&lt;p&gt;在 GoF 的定义中，创建对象的逻辑并不是封装在一个独立的、包揽大权的“工厂类”里，而是定义在了一个普通业务类（Creator）的内部，作为一个抽象方法存在。&lt;/p&gt;
&lt;p&gt;●这个模式的精髓是：父类定义业务骨架，把其中“需要实例化具体对象”的那一步，挖空成一个方法（也就是 Factory Method）。&lt;/p&gt;
&lt;p&gt;●具体的实例化工作，&lt;strong&gt;推迟（Defer）到了子类去重写这个方法&lt;/strong&gt;来实现&lt;/p&gt;</description></item><item><title>一文讲透 GoF 的 23 种设计模式之单例</title><link>https://xiaobox.github.io/p/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/</link><pubDate>Wed, 25 Feb 2026 10:13:49 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/cover.jpg" alt="Featured image of post 一文讲透 GoF 的 23 种设计模式之单例" /&gt;&lt;h1 id="一文讲透-gof-的-23-种设计模式之单例"&gt;&lt;a href="#%e4%b8%80%e6%96%87%e8%ae%b2%e9%80%8f-gof-%e7%9a%84-23-%e7%a7%8d%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e4%b9%8b%e5%8d%95%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;一文讲透 GoF 的 23 种设计模式之单例
&lt;/h1&gt;&lt;p&gt;单例模式&amp;ndash;Singleton 是创建型模式&lt;/p&gt;
&lt;h2 id="定义"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;确保一个类在一个 JVM 内只有一个实例，并提供全局访问点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/001-d2d7932d.png"&gt;&lt;/p&gt;
&lt;h2 id="什么时候用"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;什么时候用?
&lt;/h2&gt;&lt;p&gt;●配置中心、缓存管理器、日志器（有时）&lt;/p&gt;
&lt;p&gt;●需要全局共享状态/资源&lt;/p&gt;
&lt;p&gt;对于 那些初始化很贵，重复创建又特别浪费资源的场景非常合适 。&lt;/p&gt;
&lt;h2 id="不要滥用"&gt;&lt;a href="#%e4%b8%8d%e8%a6%81%e6%bb%a5%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;不要滥用
&lt;/h2&gt;&lt;p&gt;单例本质是“全局变量 + 访问入口”，会增加耦合、影响测试&lt;/p&gt;
&lt;h2 id="实现方式"&gt;&lt;a href="#%e5%ae%9e%e7%8e%b0%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;实现方式
&lt;/h2&gt;&lt;p&gt;以下为常见的 5 种实现方式对比。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;实现方式&lt;/th&gt;
 &lt;th&gt;核心机制简述&lt;/th&gt;
 &lt;th&gt;并发安全性 (线程安全)&lt;/th&gt;
 &lt;th&gt;性能表现&lt;/th&gt;
 &lt;th&gt;核心易错点 / 致命缺陷&lt;/th&gt;
 &lt;th&gt;综合推荐度&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1. 饿汉式(Eager)&lt;/td&gt;
 &lt;td&gt;类加载时立即创建静态 final 实例。&lt;/td&gt;
 &lt;td&gt;安全(JVM类加载机制保证)&lt;/td&gt;
 &lt;td&gt;高 (运行时)获取实例无锁。但可能会拖慢系统启动速度，且如果不用会浪费内存。&lt;/td&gt;
 &lt;td&gt;低实现简单，不易出错。缺点是无法进行懒加载，且难以传递动态参数进行初始化。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2. 懒汉式(同步方法)&lt;/td&gt;
 &lt;td&gt;在 getInstance 方法上加 synchronized 锁。&lt;/td&gt;
 &lt;td&gt;安全(粗粒度锁保证)&lt;/td&gt;
 &lt;td&gt;非常低每次调用 getInstance 都要发生线程竞争和锁获取，高并发下是严重的性能瓶颈。&lt;/td&gt;
 &lt;td&gt;低实现简单。主要的&amp;quot;错&amp;quot;是选择了这种低效的方案。&lt;/td&gt;
 &lt;td&gt;⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3. 双重检查锁(DCL)&lt;/td&gt;
 &lt;td&gt;两次判空 + 同步代码块 + volatile 关键字。&lt;/td&gt;
 &lt;td&gt;安全 (有前提)必须在实例变量上加 volatile 禁止指令重排序。&lt;/td&gt;
 &lt;td&gt;高只在第一次初始化时加锁，后续调用无锁。实现了高性能的懒加载。&lt;/td&gt;
 &lt;td&gt;极高 (致命)最常见的错误是忘记加 volatile 关键字。这会导致多线程环境下，某个线程可能会拿到一个&amp;quot;半初始化&amp;quot;的对象，引发难以排查的 Bug。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4. 静态内部类(Holder模式)&lt;/td&gt;
 &lt;td&gt;利用 JVM 加载外部类时不加载静态内部类的特性实现懒加载。&lt;/td&gt;
 &lt;td&gt;安全(JVM类加载机制保证)&lt;/td&gt;
 &lt;td&gt;高既实现了懒加载，又在获取实例时没有任何锁机制，性能优异。&lt;/td&gt;
 &lt;td&gt;低非常规整的写法。唯一需要注意的是要确保构造函数私有，防止外部意外实例化。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐⭐⭐ (手动实现首选)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5. 枚举(Enum)&lt;/td&gt;
 &lt;td&gt;利用 Java 枚举类型的特殊语法和底层实现。&lt;/td&gt;
 &lt;td&gt;安全 (天然)(JVM 层面保障，防御反射和序列化攻击)&lt;/td&gt;
 &lt;td&gt;高类似于饿汉式，类加载时完成初始化，运行时无锁。&lt;/td&gt;
 &lt;td&gt;极低代码最简洁，几乎不可能写错。缺点是无法继承其他类，且在语义上用来做复杂业务对象时显得突兀。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐⭐⭐ (最安全简洁)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;重点说明两种实现方式：枚举和静态内部类。&lt;/p&gt;
&lt;h3 id="枚举"&gt;&lt;a href="#%e6%9e%9a%e4%b8%be" class="header-anchor"&gt;&lt;/a&gt;枚举
&lt;/h3&gt;&lt;p&gt;这是 Java 最简洁实现。Java 的 Enum 在语言层面有一些特殊保证（例如不会被克隆），这也是它常被用来实现单例的原因之一。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;prod&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;setEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用枚举（enum）来实现单例模式，被《Effective Java》的作者 Joshua Bloch 称为 &lt;strong&gt;“实现单例模式的最佳方法”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它之所以备受推崇，是因为它用极其简洁的代码，完美解决了传统单例模式面临的线程安全、序列化破坏和反射破坏三大难题&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理一：利用 JVM 类加载机制保证“线程安全”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在传统的懒汉式单例中，为了保证多线程下只创建一个实例，我们需要写复杂的“双重检查锁（Double-Checked Locking）”并加上 volatile 关键字。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;当你定义 INSTANCE 时，编译器底层实际会把它转化为类似这样的代码：&lt;/p&gt;
&lt;p&gt;⚡ java片段&lt;code&gt;public static final AppConfig INSTANCE = new AppConfig();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Java 虚拟机（JVM）在加载类的时候，会利用底层的类加载机制保证静态成员的初始化是绝对线程安全的。在这个类被加载到内存时，JVM 会自动实例化 INSTANCE 且只实例化一次，整个过程由 JVM 内部加锁保证同步，不需要你手动写任何并发控制代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理二：天生防御“反射攻击”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;传统的单例模式有一个致命弱点：恶意代码可以通过 Java 的反射机制（Reflection）把私有构造函数设置为可见（setAccessible(true)），从而强行 new 出新的实例，打破单例。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;Java 的反射 API 从源码级别就直接“封杀”了通过反射创建枚举实例的可能性。如果你去看 Constructor.newInstance() 的 Java 底层源码，会发现有一段明确的校验逻辑：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;java片段if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getModifiers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Modifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Cannot reflectively create enum objects&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，&lt;strong&gt;一旦 JVM 发现你要用反射去创建枚举类的对象，就会直接抛出异常&lt;/strong&gt;，从根本上杜绝了反射攻击。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理三：天生防御“序列化破坏”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;传统的单例对象如果实现了 Serializable 接口，在进行网络传输或持久化到磁盘再反序列化读取回来时，默认会重新分配内存，生成一个全新的对象。传统做法是必须手动写一个 readResolve() 方法来返回原实例。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;Java 规范对枚举的序列化有特殊的规定。枚举在序列化的时候，仅仅是将枚举常量的名称（name）输出到了结果中；在反序列化的时候，Java 会调用 java.lang.Enum.valueOf() 方法，通过名字去查找并返回内存中已经存在的那个常量对象。&lt;/p&gt;
&lt;p&gt;因此，无论你反序列化多少次，拿到的永远是内存里的同一个 INSTANCE 对象。&lt;/p&gt;
&lt;p&gt;总结来说：枚举单例的核心原理就是 &lt;strong&gt;直接利用 Java 语言底层的机制&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;●用 JVM 类加载机制 搞定了线程安全。&lt;/p&gt;
&lt;p&gt;●用 反射 API 的硬编码拦截 搞定了反射破坏。&lt;/p&gt;
&lt;p&gt;●用 特殊的名称匹配机制 搞定了序列化破坏。&lt;/p&gt;
&lt;p&gt;在理论上，枚举单例确实是“最完美”的单例实现；但在实际的工程代码中，它的出场率确实不高。这并不是因为枚举本身有 bug，而是因为它在现代工程架构、面向对象设计理念以及测试友好度上，存在一些不可避免的局限性&lt;/p&gt;
&lt;p&gt;具体来说，有以下几个核心原因：&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;现代框架（如 Spring）接管了单例的管理&lt;/strong&gt; 这是最根本的原因。在现代 Java 工程中（尤其是企业级开发），我们几乎不再手动编写任何单例模式了。 我们广泛使用 Spring/Spring Boot 这样的依赖注入（DI）框架。在 Spring 中，你只需要在一个普通的类上加上 @Service、@Component 或 @Configuration 注解，Spring 容器（IoC Container）就会默认将其作为一个单例来管理。框架不仅帮你保证了单例，还能帮你自动注入其他依赖（如数据库连接、其他服务），这比用枚举手写单例要强大、灵活得多。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;违反了“语义”和开发者的直觉&lt;/strong&gt; 代码不仅是给机器运行的，更是给人读的。 枚举的本来语义：代表一组固定的常量集合（如星期、颜色、订单状态）。单例的语义：通常是一个拥有复杂业务逻辑的管理类（如 UserManager、DatabaseConnectionPool）。&lt;/p&gt;
&lt;p&gt;如果把一个复杂的业务服务写成 enum，会让接手代码的其他开发者感到困惑，这违反了“最小惊讶原则（Principle of Least Astonishment）”。感觉就像是“为了用单例模式而强行用枚举”。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;面向对象特性的缺失（无法继承）&lt;/strong&gt; Java 规定，所有的枚举类都隐式继承了 java.lang.Enum。因为 Java 不支持多重继承，这意味着你的枚举单例不能再继承任何其他的父类。 如果你的架构需要 AppConfig 继承一个 BaseConfig 类来复用代码，枚举单例直接就做不到。 虽然枚举可以实现接口（implements Interface），但在需要共享基类代码的场景下，它的表现非常无力。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;传参初始化非常困难&lt;/strong&gt; 在工程实践中，单例对象在初始化时往往需要外部参数。比如，一个数据库连接池单例，在启动时需要读取配置文件里的 url 和 password。 普通的单例模式或 Spring 管理的 Bean，可以在运行时读取配置后，再进行初始化。 枚举常量的实例化是在类加载的最早期进行的，这个时候你很难把运行时的参数优雅地传递给枚举的构造函数。&lt;/p&gt;
&lt;p&gt;5.&lt;strong&gt;极难进行单元测试（Mock）&lt;/strong&gt; 在做单元测试时，我们经常需要把某些依赖的单例对象“Mock（模拟）”掉（比如使用 Mockito），以隔离测试环境。 普通类别的单例很容易被 Mock 框架替换。但是，枚举是静态的全局常量，它的生命周期和类加载器绑定。在测试中强行替换枚举实例极其困难，容易导致测试用例之间互相污染。&lt;/p&gt;
&lt;p&gt;在实际工程中：&lt;/p&gt;
&lt;p&gt;●如果你要写一个完全无状态、不需要继承、不依赖外部配置的纯工具类/简单配置类，用枚举单例确实不错。&lt;/p&gt;
&lt;p&gt;●但对于包含业务逻辑、需要依赖注入、需要被测试的类，交给 Spring 等框架去管理才是工业界的最佳实践。&lt;/p&gt;
&lt;h3 id="静态内部类"&gt;&lt;a href="#%e9%9d%99%e6%80%81%e5%86%85%e9%83%a8%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;静态内部类
&lt;/h3&gt;&lt;p&gt;如果你不想用枚举，又想要一个&lt;strong&gt;既能延迟加载（懒汉式），又绝对线程安全，还能完美避开繁琐的加锁（synchronized）&lt;/strong&gt; 的单例，静态内部类是最佳选择。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. 私有化构造函数，防止外部 new&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 可选：在这里加上防御反射攻击的代码&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SingletonHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;不允许通过反射创建单例！&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. 核心：定义一个私有的静态内部类&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这个类直到被调用时才会被 JVM 加载&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SingletonHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 由 JVM 保证这里的实例化是绝对线程安全的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INSTANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. 提供全局访问点&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 只有在调用这里时，SingletonHolder 才会被加载，从而实例化 INSTANCE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SingletonHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么它很巧妙？&lt;/p&gt;
&lt;p&gt;●懒加载（Lazy Loading）：当你加载 DatabaseConnectionPool 这个类时，内部类 SingletonHolder 并不会被立刻加载。只有当你真正调用 getInstance() 方法时，内部类才会被加载，对象才会被创建。这就节省了内存。&lt;/p&gt;
&lt;p&gt;●零并发负担：它没有使用任何 synchronized 或者 volatile 关键字。它完全将线程安全的控制权交给了 JVM 底层的类加载机制（JVM 在加载一个类时，会自动加锁保证全局唯一）。&lt;/p&gt;
&lt;h2 id="spring-是如何实现单例的"&gt;&lt;a href="#spring-%e6%98%af%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e5%8d%95%e4%be%8b%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;Spring 是如何实现单例的？
&lt;/h2&gt;&lt;p&gt;Spring 里的单例（Singleton）和我们在《设计模式》书里学到的单例，在概念和实现思路上有很大的不同。&lt;/p&gt;
&lt;p&gt;●传统单例（GoF单例）：保证在一个 JVM（准确地说是类加载器）级别，某个类只有一个实例。类自己控制自己的实例化。&lt;/p&gt;
&lt;p&gt;●Spring 单例：保证在一个 Spring IoC 容器（ApplicationContext）内部，某个指定的 Bean 名称只有一个实例。它是由 Spring 框架来统一管理的。&lt;/p&gt;
&lt;p&gt;Spring 实现单例的核心原理可以概括为：&lt;strong&gt;单例注册表（Singleton Registry）&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="1-核心数据结构concurrenthashmap"&gt;&lt;a href="#1-%e6%a0%b8%e5%bf%83%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84concurrenthashmap" class="header-anchor"&gt;&lt;/a&gt;1. 核心数据结构：ConcurrentHashMap
&lt;/h3&gt;&lt;p&gt;如果你翻开 Spring 的底层源码（DefaultSingletonBeanRegistry 类），你会发现 Spring 管理单例的本质，就是一个大大的缓存 Map：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ java片段// Spring 源码中的 &amp;#34;一级缓存&amp;#34;，存放所有完全初始化好的单例 Bean
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;private final Map&amp;lt;String, Object&amp;gt; singletonObjects = new ConcurrentHashMap&amp;lt;&amp;gt;(256);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Spring 的单例其实就是把创建好的对象塞进了一个线程安全的 ConcurrentHashMap 里。Key 是 Bean 的名字（通常是类名首字母小写），Value 就是这个类的实例对象。&lt;/p&gt;
&lt;h3 id="2-spring-创建单例的流程"&gt;&lt;a href="#2-spring-%e5%88%9b%e5%bb%ba%e5%8d%95%e4%be%8b%e7%9a%84%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;2. Spring 创建单例的流程
&lt;/h3&gt;&lt;p&gt;当你在代码里注入一个单例（比如通过 @Autowired），或者调用 context.getBean(&amp;ldquo;myService&amp;rdquo;) 时，Spring 大致会经历以下步骤：&lt;/p&gt;
&lt;p&gt;1.查缓存：Spring 首先会去 singletonObjects 这个 Map 里查，看看有没有叫 &amp;ldquo;myService&amp;rdquo; 的对象。&lt;/p&gt;
&lt;p&gt;2.有则返回：如果 Map 里有，说明已经创建过了，直接把这个对象返回给你。这就是单例的体现。&lt;/p&gt;
&lt;p&gt;3.无则创建并加锁：如果 Map 里没有，Spring 就会准备创建它。为了保证在多线程环境下只有一个线程能去创建这个 Bean，Spring 会对这个 Bean 的名字进行加锁（通常是通过对全局单例集合的锁或者特定的互斥锁来实现同步）。&lt;/p&gt;
&lt;p&gt;4.实例化与初始化：Spring 通过反射调用构造函数把对象 new 出来，然后进行属性填充（依赖注入），再调用 @PostConstruct 等初始化方法。&lt;/p&gt;
&lt;p&gt;5.放入 Map 并返回：最后，把完全准备好的对象放进 singletonObjects 这个 ConcurrentHashMap 里，然后返回给你。以后所有对这个 Bean 的请求，都直接从 Map 里拿。&lt;/p&gt;
&lt;h3 id="3-补充循环依赖的杀手锏三级缓存"&gt;&lt;a href="#3-%e8%a1%a5%e5%85%85%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96%e7%9a%84%e6%9d%80%e6%89%8b%e9%94%8f%e4%b8%89%e7%ba%a7%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;3. 补充：循环依赖的杀手锏“三级缓存”
&lt;/h3&gt;&lt;p&gt;Spring 在管理单例时，还要解决一个传统单例很难解决的问题——循环依赖（比如 A 依赖 B，B 又依赖 A）。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，Spring 其实并没有只用一个 Map，而是用了三个 Map（传说中的三级缓存）：&lt;/p&gt;
&lt;p&gt;●一级缓存（singletonObjects）：存完整的、可用的单例对象。&lt;/p&gt;
&lt;p&gt;●二级缓存（earlySingletonObjects）：存半成品对象（刚 new 出来，但还没注入属性的对象），用于提前暴露自己，打破循环。&lt;/p&gt;
&lt;p&gt;●三级缓存（singletonFactories）：存对象工厂，用于在需要时生成代理对象（比如处理 AOP 切面）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/002-5ae19680.svg"&gt;&lt;/p&gt;
&lt;p&gt;结合上面的图，核心过程如下：&lt;/p&gt;
&lt;p&gt;第一阶段：A 的创建与曝光&lt;/p&gt;
&lt;p&gt;1.调用 getBean(A)：Spring 容器开始创建 Bean A。&lt;/p&gt;
&lt;p&gt;2.实例化 A：调用构造函数，A 对象在内存中诞生，但属性（如 B）还是 null。&lt;/p&gt;
&lt;p&gt;3.暴露三级缓存：Spring 将 A 的工厂对象放入 三级缓存 (singletonFactories)。这是解决循环依赖的关键一步，意味着此时如果有其他对象引用 A，可以通过这个工厂拿到 A 的引用。&lt;/p&gt;
&lt;p&gt;第二阶段：A 填充属性，触发 B 的创建&lt;/p&gt;
&lt;p&gt;4.填充属性 B：A 发现自己依赖 B，于是暂停自己，转而去创建 B。&lt;/p&gt;
&lt;p&gt;第三阶段：B 的创建与获取 A&lt;/p&gt;
&lt;p&gt;5.实例化 B：B 对象诞生，属性（如 A）还是 null。&lt;/p&gt;
&lt;p&gt;6.暴露三级缓存：将 B 的工厂放入三级缓存。&lt;/p&gt;
&lt;p&gt;7.填充属性 A：B 发现自己依赖 A，于是尝试去缓存找 A。&lt;/p&gt;
&lt;p&gt;第四阶段：B 从缓存中找到 A (核心转折)&lt;/p&gt;
&lt;p&gt;8.查找缓存：&lt;/p&gt;
&lt;p&gt;●找一级缓存？没有（A 还没彻底完工）。&lt;/p&gt;
&lt;p&gt;●找二级缓存？没有（还没人提取过 A 的早期引用）。&lt;/p&gt;
&lt;p&gt;●找三级缓存？有了！&lt;/p&gt;
&lt;p&gt;9.升级缓存：&lt;/p&gt;
&lt;p&gt;●B 调用三级缓存中的工厂方法，拿到 A 的早期引用。&lt;/p&gt;
&lt;p&gt;●重点：如果 A 配置了 AOP（比如事务管理），这个工厂会提前生成 A 的代理对象。&lt;/p&gt;
&lt;p&gt;●将 A 的早期引用放入 二级缓存 (earlySingletonObjects)，并从三级缓存移除。&lt;/p&gt;
&lt;p&gt;10.B 完成：B 拿到了 A 的引用，完成属性填充和初始化，放入 一级缓存。&lt;/p&gt;
&lt;p&gt;第五阶段：A 完成&lt;/p&gt;
&lt;p&gt;11.A 获取 B：B 已经创建好了，A 顺利拿到 B 的引用。&lt;/p&gt;
&lt;p&gt;12.A 完成：A 完成属性填充和初始化，放入 一级缓存。&lt;/p&gt;</description></item><item><title>RAG 的五大范式</title><link>https://xiaobox.github.io/p/2025-03-11-rag-de-wu-da-fan-shi/</link><pubDate>Tue, 11 Mar 2025 10:27:02 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-03-11-rag-de-wu-da-fan-shi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/cover.jpg" alt="Featured image of post RAG 的五大范式" /&gt;&lt;h2 id="naive-rag-朴素-rag"&gt;&lt;a href="#naive-rag-%e6%9c%b4%e7%b4%a0-rag" class="header-anchor"&gt;&lt;/a&gt;Naive RAG (朴素 RAG)
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/001-60de0142.png"&gt;&lt;/p&gt;
&lt;h3 id="定义"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/002-c069d345.png"&gt;&lt;/p&gt;
&lt;h3 id="核心思想"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3" class="header-anchor"&gt;&lt;/a&gt;核心思想
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/003-f2404cdd.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将文档分块、向量化并存入向量数据库&lt;/li&gt;
&lt;li&gt;用户查询也向量化，并在数据库中检索最相似的文档块&lt;/li&gt;
&lt;li&gt;最后，将查询和检索到的文档块一起输入 LLM 生成答案&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/004-f551e788.png"&gt;&lt;/p&gt;
&lt;h3 id="优缺点分析"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9%e5%88%86%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;优缺点分析
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/005-1c4d478f.png"&gt;&lt;/p&gt;
&lt;h2 id="advanced-rag-高级-rag"&gt;&lt;a href="#advanced-rag-%e9%ab%98%e7%ba%a7-rag" class="header-anchor"&gt;&lt;/a&gt;Advanced RAG (高级 RAG)
&lt;/h2&gt;&lt;h3 id="定义-1"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89-1" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/006-d415781b.png"&gt;&lt;/p&gt;
&lt;h3 id="核心思想-1"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3-1" class="header-anchor"&gt;&lt;/a&gt;核心思想
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/007-a5446396.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;化索引（如滑动窗口、细粒度分割、元数据利用）&lt;/li&gt;
&lt;li&gt;优化查询（如查询重写、扩展、转换）&lt;/li&gt;
&lt;li&gt;优化检索结果（如重排序、过滤、压缩）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/008-5927350d.png"&gt;&lt;/p&gt;
&lt;h3 id="优缺点分析-1"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9%e5%88%86%e6%9e%90-1" class="header-anchor"&gt;&lt;/a&gt;优缺点分析
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/009-84a4971c.png"&gt;&lt;/p&gt;
&lt;h2 id="modular-rag-模块化-rag"&gt;&lt;a href="#modular-rag-%e6%a8%a1%e5%9d%97%e5%8c%96-rag" class="header-anchor"&gt;&lt;/a&gt;Modular RAG (模块化 RAG)
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/010-ffc39c97.png"&gt;&lt;/p&gt;
&lt;h3 id="定义-2"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89-2" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/011-1f2ebc0a.png"&gt;&lt;/p&gt;
&lt;h3 id="核心思想-2"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3-2" class="header-anchor"&gt;&lt;/a&gt;核心思想
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/012-fc3e63a8.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模块化设计，每个模块可独立实现和替换&lt;/li&gt;
&lt;li&gt;支持迭代、自适应、递归等多种检索模式&lt;/li&gt;
&lt;li&gt;通过组合不同模块来适应不同任务需求&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/013-9b25e145.png"&gt;&lt;/p&gt;
&lt;h3 id="优缺点分析-2"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9%e5%88%86%e6%9e%90-2" class="header-anchor"&gt;&lt;/a&gt;优缺点分析
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/014-e97e7e0b.png"&gt;&lt;/p&gt;
&lt;h2 id="graphrag-图-rag"&gt;&lt;a href="#graphrag-%e5%9b%be-rag" class="header-anchor"&gt;&lt;/a&gt;GraphRAG (图 RAG)
&lt;/h2&gt;&lt;h3 id="定义-3"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89-3" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/015-06595be8.png"&gt;&lt;/p&gt;
&lt;h3 id="核心思想-3"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3-3" class="header-anchor"&gt;&lt;/a&gt;核心思想
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/016-fe7dfaec.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建基于图的文档索引&lt;/li&gt;
&lt;li&gt;利用图数据库和查询语言进行检索&lt;/li&gt;
&lt;li&gt;将检索到的图信息与文本信息结合，输入 LLM 生成答案&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/017-7e61ba90.png"&gt;&lt;/p&gt;
&lt;h3 id="优缺点分析-3"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9%e5%88%86%e6%9e%90-3" class="header-anchor"&gt;&lt;/a&gt;优缺点分析
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/018-52ac029c.png"&gt;&lt;/p&gt;
&lt;h2 id="agentic-rag-智能体-rag"&gt;&lt;a href="#agentic-rag-%e6%99%ba%e8%83%bd%e4%bd%93-rag" class="header-anchor"&gt;&lt;/a&gt;Agentic RAG (智能体 RAG)
&lt;/h2&gt;&lt;h3 id="定义-4"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89-4" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/019-daf01754.png"&gt;&lt;/p&gt;
&lt;h3 id="核心思想-4"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3-4" class="header-anchor"&gt;&lt;/a&gt;核心思想
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/020-4287ac73.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 AI 代理管理 RAG 流程&lt;/li&gt;
&lt;li&gt;利用代理设计模式（反射、规划、工具使用、多代理协作）&lt;/li&gt;
&lt;li&gt;代理可动态协调 RAG 组件，进行推理，并根据上下文采取行动&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/021-3a77c348.png"&gt;&lt;/p&gt;
&lt;h3 id="优缺点分析-4"&gt;&lt;a href="#%e4%bc%98%e7%bc%ba%e7%82%b9%e5%88%86%e6%9e%90-4" class="header-anchor"&gt;&lt;/a&gt;优缺点分析
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/022-2d5344d3.png"&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-11-rag-de-wu-da-fan-shi/023-ef9d66f2.png"&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/html/2407.21059v1" target="_blank" rel="noopener"
 &gt;https://arxiv.org/html/2407.21059v1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>再聊聊Netty</title><link>https://xiaobox.github.io/p/2022-03-21-zai-liao-liao-netty/</link><pubDate>Mon, 21 Mar 2022 07:13:56 +0000</pubDate><guid>https://xiaobox.github.io/p/2022-03-21-zai-liao-liao-netty/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/cover.jpg" alt="Featured image of post 再聊聊Netty" /&gt;&lt;h2 id="为什么还要用-netty"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%98%e8%a6%81%e7%94%a8-netty" class="header-anchor"&gt;&lt;/a&gt;为什么还要用 Netty
&lt;/h2&gt;&lt;p&gt;既然 JAVA NIO / JAVA AIO 已经实现了各主流操作系统的底层支持，那么为什么现在主流的 JAVA NIO 技术会是 Netty 和 MINA 呢？答案很简单：因为更好用，这里举几个方面的例子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;虽然 JAVA NIO 和 JAVA AIO 框架提供了 多路复用 IO/异步 IO 的支持，但是并没有提供上层“信息格式”的良好封装。例如前两者并没有提供针对 Protocol Buffer、JSON 这些信息格式的封装，但是 Netty 框架提供了这些数据格式封装（基于责任链模式的编码和解码功能）&lt;/li&gt;
&lt;li&gt;要编写一个可靠的、易维护的、高性能的（注意它们的排序） NIO/AIO 服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="架构"&gt;&lt;a href="#%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;架构
&lt;/h2&gt;&lt;p&gt;整体架构&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/001-327b8d9b.jpg"&gt;&lt;/p&gt;
&lt;p&gt;逻辑架构&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/002-02d1c42c.jpg"&gt;&lt;/p&gt;
&lt;h2 id="线程模型"&gt;&lt;a href="#%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;线程模型
&lt;/h2&gt;&lt;p&gt;在高性能的 I/O 设计中，有两个著名的模型：Reactor 模型和 Proactor 模型，其中 Reactor 模型用于同步 I/O，而 Proactor 模型运用于异步 I/O 操作。实际上 Netty 线程模型就是 Reactor 模型的一个实现。&lt;/p&gt;
&lt;h3 id="什么是-reactor"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af-reactor" class="header-anchor"&gt;&lt;/a&gt;什么是 Reactor
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Reactor 设计模式是一种事件处理模式，用于处理由一个或多个输入同时传递到服务处理程序的服务请求。然后，服务处理程序对传入的请求进行解复用，并将它们同步分派给关联的请求处理程序。&lt;/p&gt;
&lt;p&gt;以上来自 wiki, 我们可以看到以下重点。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;事件驱动（event handling）&lt;/li&gt;
&lt;li&gt;可以处理一个或多个输入源（one or more inputs）&lt;/li&gt;
&lt;li&gt;通过 Service Handler 同步的将输入事件（Event）采用多路复用分发给相应的 Request Handler（多个）处理&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;根据 Doug Lea 在 《Scalable IO in Java 》中的介绍，Reacotr 模型主要分为三个角色：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reactor：把 IO 事件分配给对应的 handler 处理&lt;/li&gt;
&lt;li&gt;Acceptor：处理客户端连接事件&lt;/li&gt;
&lt;li&gt;Handler：处理非阻塞的任务&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Reactor 处理请求的流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;同步的等待多个事件源到达（采用 select() 实现）&lt;/li&gt;
&lt;li&gt;将事件多路分解以及分配相应的事件服务进行处理，这个分派采用 server 集中处理（dispatch）&lt;/li&gt;
&lt;li&gt;分解的事件以及对应的事件服务应用从分派服务中分离出去（handler）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为什么使用 Reactor？&lt;/p&gt;
&lt;p&gt;传统阻塞 IO 模型的不足&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个连接都需要独立线程处理，当并发数大时，创建线程数多，占用资源&lt;/li&gt;
&lt;li&gt;采用阻塞 IO 模型，连接建立后，若当前线程没有数据可读，线程会阻塞在读操作上，造成资源浪费&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;针对传统阻塞 IO 模型的两个问题，可以采用如下的方案&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基于池化思想，避免为每个连接创建线程，连接完成后将业务处理交给线程池处理&lt;/li&gt;
&lt;li&gt;基于 IO 复用模型，多个连接共用同一个阻塞对象，不用等待所有的连接。遍历到有新数据可以处理时，操作系统会通知程序，线程跳出阻塞状态，进行业务逻辑处理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reactor 线程模型分类&lt;/p&gt;
&lt;p&gt;根据 Reactor 的数量和处理资源的线程数量的不同，分为三类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单 Reactor 单线程模型&lt;/li&gt;
&lt;li&gt;单 Reactor 多线程模型&lt;/li&gt;
&lt;li&gt;多 Reactor 多线程模型&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="单-reactor-单线程模型"&gt;&lt;a href="#%e5%8d%95-reactor-%e5%8d%95%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;单 Reactor 单线程模型
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/003-1d3374b5.jpg"&gt;&lt;/p&gt;
&lt;p&gt;消息处理流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reactor 对象通过 select 监控连接事件，收到事件后通过 dispatch 进行转发。&lt;/li&gt;
&lt;li&gt;如果是连接建立的事件，则由 acceptor 接受连接，并创建 handler 处理后续事件。&lt;/li&gt;
&lt;li&gt;如果不是建立连接事件，则 Reactor 会分发调用 Handler 来响应。&lt;/li&gt;
&lt;li&gt;handler 会完成 read-&amp;gt;业务处理-&amp;gt;send 的完整业务流程。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;该线程模型的不足&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;仅用一个线程处理请求，对于多核资源机器来说是有点浪费的&lt;/li&gt;
&lt;li&gt;当处理读写任务的线程负载过高后，处理速度下降，事件会堆积，严重的会超时，可能导致客户端重新发送请求，性能越来越差&lt;/li&gt;
&lt;li&gt;单线程也会有可靠性的问题&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Redis 是由 C 语言实现的，它采用的正是「单 Reactor 单进程」的方案，因为 Redis 业务处理主要是在内存中完成，操作的速度是很快的，性能瓶颈不在 CPU 上，所以 Redis 对于命令的处理是单进程的方案。&lt;/p&gt;
&lt;p&gt;对于单线程的 Redis 来说，基于内存，且命令操作时间复杂度低，因此读写速率是非常快的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/004-03a1ef09.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/005-453f065a.jpg"&gt;&lt;/p&gt;
&lt;p&gt;针对上面的种种不足，就有了下面的线程模型&lt;/p&gt;
&lt;h3 id="单-reactor-多线程模型"&gt;&lt;a href="#%e5%8d%95-reactor-%e5%a4%9a%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;单 Reactor 多线程模型
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/006-3c1765e0.jpg"&gt;消息处理流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reactor 对象通过 Select 监控客户端请求事件，收到事件后通过 dispatch 进行分发。&lt;/li&gt;
&lt;li&gt;如果是建立连接请求事件，则由 acceptor 通过 accept 处理连接请求，然后创建一个 Handler 对象处理连接完成后续的各种事件。&lt;/li&gt;
&lt;li&gt;如果不是建立连接事件，则 Reactor 会分发调用连接对应的 Handler 来响应。&lt;/li&gt;
&lt;li&gt;Handler 只负责响应事件，不做具体业务处理，通过 Read 读取数据后，会分发给后面的 Worker 线程池进行业务处理。&lt;/li&gt;
&lt;li&gt;Worker 线程池会分配独立的线程完成真正的业务处理，然后将响应结果发给 Handler 进行处理。&lt;/li&gt;
&lt;li&gt;Handler 收到响应结果后通过 send 将响应结果返回给 Client。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;相对于第一种模型来说，在处理业务逻辑，也就是获取到 IO 的读写事件之后，交由线程池来处理，handler 收到响应后通过 send 将响应结果返回给客户端。这样可以降低 Reactor 的性能开销，从而更专注的做事件分发工作了，提升整个应用的吞吐。&lt;/p&gt;
&lt;p&gt;但是这个模型存在的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;多线程数据共享和访问比较复杂。如果子线程完成业务处理后，把结果传递给主线程 Reactor 进行发送，就会涉及共享数据的互斥和保护机制。&lt;/li&gt;
&lt;li&gt;Reactor 承担所有事件的监听和响应，只在主线程中运行，可能会存在性能问题。例如并发百万客户端连接，或者服务端需要对客户端握手进行安全认证，但是认证本身非常损耗性能。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了解决性能问题，产生了第三种主从 Reactor 多线程模型。&lt;/p&gt;
&lt;h3 id="主从-reactor-多线程模型"&gt;&lt;a href="#%e4%b8%bb%e4%bb%8e-reactor-%e5%a4%9a%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;主从 Reactor 多线程模型
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/007-6a178f92.jpg"&gt;&lt;/p&gt;
&lt;p&gt;比起第二种模型，它是将 Reactor 分成两部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;mainReactor 负责监听 server socket，用来处理网络 IO 连接建立操作，将建立的 socketChannel 指定注册给 subReactor。&lt;/li&gt;
&lt;li&gt;subReactor 主要做和建立起来的 socket 做数据交互和事件业务处理操作。通常，subReactor 个数上可与 CPU 个数等同。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nginx、Memcached 和 Netty 都是采用这种实现。&lt;/p&gt;
&lt;p&gt;消息处理流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从主线程池中随机选择一个 Reactor 线程作为 acceptor 线程，用于绑定监听端口，接收客户端连接&lt;/li&gt;
&lt;li&gt;acceptor 线程接收客户端连接请求之后创建新的 SocketChannel，将其注册到主线程池的其它 Reactor 线程上，由其负责接入认证、IP 黑白名单过滤、握手等操作&lt;/li&gt;
&lt;li&gt;步骤 2 完成之后，业务层的链路正式建立，将 SocketChannel 从主线程池的 Reactor 线程的多路复用器上摘除，重新注册到 Sub 线程池的线程上，并创建一个 Handler 用于处理各种连接事件&lt;/li&gt;
&lt;li&gt;当有新的事件发生时，SubReactor 会调用连接对应的 Handler 进行响应&lt;/li&gt;
&lt;li&gt;Handler 通过 Read 读取数据后，会分发给后面的 Worker 线程池进行业务处理&lt;/li&gt;
&lt;li&gt;Worker 线程池会分配独立的线程完成真正的业务处理，如何将响应结果发给 Handler 进行处理&lt;/li&gt;
&lt;li&gt;Handler 收到响应结果后通过 Send 将响应结果返回给 Client&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Reactor 三种模式形象比喻&lt;/p&gt;
&lt;p&gt;餐厅一般有接待员和服务员，接待员负责在门口接待顾客，服务员负责全程服务顾客&lt;/p&gt;
&lt;p&gt;Reactor 的三种线程模型可以用接待员和服务员类比&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;单 Reactor 单线程模型：接待员和服务员是同一个人，一直为顾客服务。客流量较少适合&lt;/li&gt;
&lt;li&gt;单 Reactor 多线程模型：一个接待员，多个服务员。客流量大，一个人忙不过来，由专门的接待员在门口接待顾客，然后安排好桌子后，由一个服务员一直服务，一般每个服务员负责一片中的几张桌子&lt;/li&gt;
&lt;li&gt;多 Reactor 多线程模型：多个接待员，多个服务员。这种就是客流量太大了，一个接待员忙不过来了&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="什么是-proactor"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af-proactor" class="header-anchor"&gt;&lt;/a&gt;什么是 Proactor
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/008-c0d542c0.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Proactor 正是采用了异步 I/O 技术，所以被称为异步网络模型。&lt;/p&gt;
&lt;p&gt;无论是 Reactor，还是 Proactor，都是一种基于「事件分发」的网络编程模式，区别在于 Reactor 模式是基于「待完成」的 I/O 事件，而 Proactor 模式则是基于「已完成」的 I/O 事件。&lt;/p&gt;
&lt;p&gt;在 Linux 下的异步 I/O 是不完善的， &lt;code&gt;aio&lt;/code&gt; 系列函数是由 POSIX 定义的异步操作接口，不是真正的操作系统级别支持的，而是在用户空间模拟出来的异步，并且仅仅支持基于本地文件的 aio 异步操作，网络编程中的 socket 是不支持的，这也使得基于 Linux 的高性能网络程序都是使用 Reactor 方案。&lt;/p&gt;
&lt;p&gt;而 Windows 里实现了一套完整的支持 socket 的异步编程接口，这套接口就是 &lt;code&gt;IOCP&lt;/code&gt;，是由操作系统级别实现的异步 I/O，真正意义上异步 I/O，因此在 Windows 里实现高性能网络程序可以使用效率更高的 Proactor 方案。&lt;/p&gt;
&lt;h3 id="线程模型的应用"&gt;&lt;a href="#%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b%e7%9a%84%e5%ba%94%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;线程模型的应用
&lt;/h3&gt;&lt;p&gt;Redis&lt;/p&gt;
&lt;p&gt;Redis 是典型的单 Reactor 单线程类型。Redis 主要通过 aeMain、aeProcessEvents，以及 aeCreateFileEvent 三个关键函数来实现 Reactor 模型，对源码 (Linux 系统）的整理如下：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;源码文件&lt;/th&gt;
 &lt;th&gt;函数&lt;/th&gt;
 &lt;th&gt;被调用点&lt;/th&gt;
 &lt;th&gt;主要功能&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;ae.c/ae.h&lt;/td&gt;
 &lt;td&gt;aeMain()&lt;/td&gt;
 &lt;td&gt;server.c 的 main()&lt;/td&gt;
 &lt;td&gt;事件捕获，分发和循环处理&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ae.c/ae.h&lt;/td&gt;
 &lt;td&gt;aeProcessEvents()&lt;/td&gt;
 &lt;td&gt;ae.c 的 aeMain()&lt;/td&gt;
 &lt;td&gt;根据事件类型进行响应的处理&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ae.c/ae.h&lt;/td&gt;
 &lt;td&gt;aeApiPoll()&lt;/td&gt;
 &lt;td&gt;ae.c 的 aeProcessEvents()&lt;/td&gt;
 &lt;td&gt;调用操作系统的 IO 多路方法&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Linux 内核文件&lt;/td&gt;
 &lt;td&gt;epoll_wait()&lt;/td&gt;
 &lt;td&gt;ae.c 的 aeApiPoll()&lt;/td&gt;
 &lt;td&gt;检测并返回内核中网络 IO 事件&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;nginx&lt;/p&gt;
&lt;p&gt;nginx 是多进程模型，master 进程不处理网络 IO，每个 Wroker 进程是一个独立的单 Reacotr 单线程模型。详情参考官方文档：Inside NGINX: Designed for Performance &amp;amp; Scalability&lt;/p&gt;
&lt;p&gt;Netty&lt;/p&gt;
&lt;p&gt;Netty 的线程模型主要是基于 Reactor 模型，但是可以灵活配置，单 reactor 单线程，单 reactor 多线程，和多 reactor 多线程模型。&lt;/p&gt;
&lt;p&gt;kafka&lt;/p&gt;
&lt;p&gt;kafka 采用的是主从 Reactor 多线程模型，因为 Kafka 主要与磁盘 IO 交互，因此真正的读写数据不是从 Reactor 处理的，而是有一个 worker 线程池，专门处理磁盘 IO，从 Reactor 负责网络 IO，然后把任务交给 worker 线程池处理。&lt;/p&gt;
&lt;h2 id="netty-线程模型"&gt;&lt;a href="#netty-%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;Netty 线程模型
&lt;/h2&gt;&lt;p&gt;上文说 Netty 就是采用 Reactor 模型实现的。下面是 Netty 使用中很常见的一段代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EventLoopGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bossGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NioEventLoopGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EventLoopGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;workerGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NioEventLoopGroup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServerBootstrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServerBootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bossGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;workerGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NioServerSocketChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChannelOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TCP_NODELAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AttributeKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;childAttr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;childAttrValue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServerHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ChannelInitializer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SocketChannel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;initChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketChannel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ChannelFuture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;8888&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;closeFuture&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bossGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdownGracefully&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;workerGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdownGracefully&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;boss 线程池作用：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;接收客户端的连接，初始化 Channel 参数。&lt;/li&gt;
&lt;li&gt;将链路状态变更时间通知给 ChannelPipeline。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;worker 线程池作用：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;异步读取通信对端的数据报，发送读事件到 ChannelPipeline。&lt;/li&gt;
&lt;li&gt;异步发送消息到通信对端，调用 ChannelPipeline 的消息发送接口。&lt;/li&gt;
&lt;li&gt;执行系统调用 Task。&lt;/li&gt;
&lt;li&gt;执行定时任务 Task。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过配置 boss 和 worker 线程池的线程个数以及是否共享线程池等方式，Netty 的线程模型可以在以上三种 Reactor 模型之间进行切换。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/009-88eb9d75.jpg"&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/010-2d671bfc.jpg"&gt;&lt;/p&gt;
&lt;p&gt;netty 通过 Reactor 模型基于多路复用器接收并处理用户请求，内部实现了两个线程池，boss 线程池和 work 线程池：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;其中 boss 线程池的线程负责处理请求的 accept 事件，当接收到 accept 事件的请求时，把对应的 socket 封装到一个 NioSocketChannel 中，并交给 worker 线程池&lt;/li&gt;
&lt;li&gt;其中 worker 线程池负责请求的 read 和 write 事件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/011-bec30573.jpg"&gt;&lt;/p&gt;
&lt;h2 id="零拷贝"&gt;&lt;a href="#%e9%9b%b6%e6%8b%b7%e8%b4%9d" class="header-anchor"&gt;&lt;/a&gt;零拷贝
&lt;/h2&gt;&lt;p&gt;Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS，使用堆外内存直接读写。&lt;/p&gt;
&lt;p&gt;Netty 中的零拷贝和传统 Linux 的零拷贝不太一样。Netty 中的零拷贝技术除了操作系统级别的功能封装，更多的是面向用户态的数据操作优化，主要体现在以下 5 个方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;堆外内存，避免 JVM 堆内存到堆外内存的数据拷贝。&lt;/li&gt;
&lt;li&gt;CompositeByteBuf 类，可以组合多个 Buffer 对象合并成一个逻辑上的对象，避免通过传统内存拷贝的方式将几个 Buffer 合并成一个大的 Buffer。&lt;/li&gt;
&lt;li&gt;通过 Unpooled.wrappedBuffer 可以将 byte 数组包装成 ByteBuf 对象，包装过程中不会产生内存拷贝。&lt;/li&gt;
&lt;li&gt;ByteBuf.slice 操作与 Unpooled.wrappedBuffer 相反，slice 操作可以将一个 ByteBuf 对象切分成多个 ByteBuf 对象，切分过程中不会产生内存拷贝，底层共享一个 byte 数组的存储空间。&lt;/li&gt;
&lt;li&gt;Netty 使用 FileRegion 实现文件传输，FileRegion 底层封装了 &lt;code&gt;FileChannel#transferTo()&lt;/code&gt; 方法，可以将文件缓冲区的数据直接传输到目标 Channel，避免内核缓冲区和用户态缓冲区之间的数据拷贝，这属于操作系统级别的零拷贝。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;堆外内存&lt;/p&gt;
&lt;p&gt;如果在 JVM 内部执行 I/O 操作时，必须将数据拷贝到堆外内存，才能执行系统调用。这是所有 VM 语言都会存在的问题。那么为什么操作系统不能直接使用 JVM 堆内存进行 I/O 的读写呢？主要有两点原因：第一，操作系统并不感知 JVM 的堆内存，而且 JVM 的内存布局与操作系统所分配的是不一样的，操作系统并不会按照 JVM 的行为来读写数据。第二，同一个对象的内存地址随着 JVM GC 的执行可能会随时发生变化，例如 JVM GC 的过程中会通过压缩来减少内存碎片，这就涉及对象移动的问题了。&lt;/p&gt;
&lt;p&gt;Netty 在进行 I/O 操作时都是使用的堆外内存，可以避免数据从 JVM 堆内存到堆外内存的拷贝。&lt;/p&gt;
&lt;h2 id="nio-epoll-bug"&gt;&lt;a href="#nio-epoll-bug" class="header-anchor"&gt;&lt;/a&gt;NIO epoll bug
&lt;/h2&gt;&lt;p&gt;JDK 的 NIO 类库有一个 epoll 死循环 bug，它会导致 Selector 空轮询，IO 线程 CPU 达到 100%，严重影响系统运行。&lt;/p&gt;
&lt;p&gt;netty 从 api 使用层面对该 bug 进行了规避解决， 重建 Selector。当发生 epoll bug，则创建一个新的 Selector，将出现 bug 的 Selector 上的 channel 重新注册到新的 Selector 上，关闭 bug 的 Selector，使用新的 Selector 进行替换。&lt;/p&gt;
&lt;p&gt;具体策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对 Selector 的 select 操作周期进行统计。&lt;/li&gt;
&lt;li&gt;每完成一次空的 select 操作进行一次计数。&lt;/li&gt;
&lt;li&gt;在某个周期内如果连续 N 次空轮询，则说明触发了 JDK NIO 的 epoll 死循环 bug。&lt;/li&gt;
&lt;li&gt;创建新的 Selector，将出现 bug 的 Selector 上的 channel 重新注册到新的 Selector 上。&lt;/li&gt;
&lt;li&gt;关闭 bug 的 Selector，使用新的 Selector 进行替换。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果发生 epoll 死循环 bug，那么当前 I/O 线程将停下来进行 bug 的修复，然后再继续进行逻辑处理，bug 的修复是非阻塞操作，处理速度非常快；而且 netty 线程池中一般设置有多个 I/O 线程，其中某个 I/O 线程中的 Selector 触发 bug 并不会影响其他 I/O 线程运行，所以 netty 通过这种策略，在几乎不影响性能的情况下从 api 使用层面规避解决了该 bug。&lt;/p&gt;
&lt;h2 id="粘包拆包"&gt;&lt;a href="#%e7%b2%98%e5%8c%85%e6%8b%86%e5%8c%85" class="header-anchor"&gt;&lt;/a&gt;粘包拆包
&lt;/h2&gt;&lt;p&gt;TCP 层可能会出现当次接收到的数据是不完整数据的情况。出现粘包可能的原因有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;发送方每次写入数据 &amp;lt; 套接字缓冲区大小；&lt;/li&gt;
&lt;li&gt;接收方读取套接字缓冲区数据不够及时。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;出现半包的可能原因有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;发送方每次写入数据 &amp;gt; 套接字缓冲区大小；&lt;/li&gt;
&lt;li&gt;发送的数据大于协议 MTU，所以必须要拆包。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;解决问题肯定不是在 4 层来做而是在应用层，通过定义通信协议来解决粘包和拆包的问题。发送方 和 接收方约定某个规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当发生粘包的时候通过某种约定来拆包；&lt;/li&gt;
&lt;li&gt;如果在拆包，通过某种约定来将数据组成一个完整的包处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="业界常用解决方案"&gt;&lt;a href="#%e4%b8%9a%e7%95%8c%e5%b8%b8%e7%94%a8%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;业界常用解决方案
&lt;/h4&gt;&lt;p&gt;定长协议&lt;/p&gt;
&lt;p&gt;指定一个报文具有固定长度。比如约定一个报文的长度是 5 字节，那么：报文：1234，只有 4 字节，但是还差一个怎么办呢，不足部分用空格补齐。就变为：1234 。如果不补齐空格，那么就会读到下一个报文的字节来填充上一个报文直到补齐为止，这样粘包了。定长协议的优点是使用简单，缺点很明显：浪费带宽。&lt;/p&gt;
&lt;p&gt;Netty 中提供了 &lt;code&gt;FixedLengthFrameDecoder&lt;/code&gt; ，支持把固定的长度的字节数当做一个完整的消息进行解码。&lt;/p&gt;
&lt;p&gt;特殊字符分割协议&lt;/p&gt;
&lt;p&gt;很好理解，在每一个你认为是一个完整的包的尾部添加指定的特殊字符，比如：\n，\r 等等。需要注意的是：约定的特殊字符要保证唯一性，不能出现在报文的正文中，否则就将正文一分为二了。&lt;/p&gt;
&lt;p&gt;Netty 中提供了 &lt;code&gt;DelimiterBasedFrameDecoder&lt;/code&gt; 根据特殊字符进行解码，&lt;code&gt;LineBasedFrameDecoder&lt;/code&gt;默认以换行符作为分隔符。&lt;/p&gt;
&lt;p&gt;变长协议&lt;/p&gt;
&lt;p&gt;变长协议的核心就是：将消息分为消息头和消息体，消息头中标识当前完整的消息体长度。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;发送方在发送数据之前先获取数据的二进制字节大小，然后在消息体前面添加消息大小；&lt;/li&gt;
&lt;li&gt;接收方在解析消息时先获取消息大小，之后必须读到该大小的字节数才认为是完整的消息。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Netty 中提供了 &lt;code&gt;LengthFieldBasedFrameDecoder&lt;/code&gt; ，通过&lt;code&gt;LengthFieldPrepender&lt;/code&gt; 来给实际的消息体添加 length 字段。&lt;/p&gt;
&lt;h4 id="netty-提供的能力"&gt;&lt;a href="#netty-%e6%8f%90%e4%be%9b%e7%9a%84%e8%83%bd%e5%8a%9b" class="header-anchor"&gt;&lt;/a&gt;Netty 提供的能力
&lt;/h4&gt;&lt;p&gt;为了解决网络数据流的拆包粘包问题，Netty 为我们内置了如下的解码器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ByteToMessageDecoder：如果想实现自己的半包解码器，实现该类；&lt;/li&gt;
&lt;li&gt;MessageToMessageDecoder：一般作为二次解码器，当我们在 ByteToMessageDecoder 将一个 bytes 数组转换成一个 java 对象的时候，我们可能还需要将这个对象进行二次解码成其他对象，我们就可以继承这个类；&lt;/li&gt;
&lt;li&gt;LineBasedFrameDecoder：通过在包尾添加回车换行符 &lt;code&gt;\r\n&lt;/code&gt; 来区分整包消息；&lt;/li&gt;
&lt;li&gt;StringDecoder：字符串解码器；&lt;/li&gt;
&lt;li&gt;DelimiterBasedFrameDecoder：特殊字符作为分隔符来区分整包消息；&lt;/li&gt;
&lt;li&gt;FixedLengthFrameDecoder：报文大小固定长度，不够空格补全；&lt;/li&gt;
&lt;li&gt;ProtoBufVarint32FrameDecoder：通过 Protobuf 解码器来区分整包消息；&lt;/li&gt;
&lt;li&gt;ProtobufDecoder：Protobuf 解码器；&lt;/li&gt;
&lt;li&gt;LengthFieldBasedFrameDecoder：指定长度来标识整包消息，通过在包头指定整包长度来约定包长。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Netty 还内置了如下的编码器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ProtobufEncoder：Protobuf 编码器；&lt;/li&gt;
&lt;li&gt;MessageToByteEncoder：将 Java 对象编码成 ByteBuf；&lt;/li&gt;
&lt;li&gt;MessageToMessageEncoder：如果不想将 Java 对象编码成 ByteBuf，而是自定义类就继承这个；&lt;/li&gt;
&lt;li&gt;LengthFieldPrepender：LengthFieldPrepender 是一个非常实用的工具类，如果我们在发送消息的时候采用的是：消息长度字段+原始消息的形式，那么我们就可以使用 LengthFieldPrepender。这是因为 LengthFieldPrepender 可以将待发送消息的长度（二进制字节长度）写到 ByteBuf 的前两个字节。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/012-35833b92.jpg"&gt;&lt;/p&gt;
&lt;h3 id="udp-是否会发生粘包或拆包的现象呢"&gt;&lt;a href="#udp-%e6%98%af%e5%90%a6%e4%bc%9a%e5%8f%91%e7%94%9f%e7%b2%98%e5%8c%85%e6%88%96%e6%8b%86%e5%8c%85%e7%9a%84%e7%8e%b0%e8%b1%a1%e5%91%a2" class="header-anchor"&gt;&lt;/a&gt;UDP 是否会发生粘包或拆包的现象呢？
&lt;/h3&gt;&lt;p&gt;答案是不会。&lt;/p&gt;
&lt;p&gt;UDP 是基于报文发送的，从 UDP 的帧结构可以看出，在 UDP 首部采用了 16bit 来指示 UDP 数据报文的长度，因此在应用层能很好的将不同的数据报文区分开，从而避免粘包和拆包的问题。&lt;/p&gt;
&lt;p&gt;而TCP 是基于字节流的，虽然应用层和 TCP 传输层之间的数据交互是大小不等的数据块，但是 TCP 把这些数据块仅仅看成一连串无结构的字节流，没有边界；另外从 TCP 的帧结构也可以看出，在 TCP 的首部没有表示数据长度的字段，基于上面两点，在使用 TCP 传输数据时，才有粘包或者拆包现象发生的可能。&lt;/p&gt;
&lt;h2 id="api"&gt;&lt;a href="#api" class="header-anchor"&gt;&lt;/a&gt;API
&lt;/h2&gt;&lt;h3 id="bytebuf"&gt;&lt;a href="#bytebuf" class="header-anchor"&gt;&lt;/a&gt;ByteBuf
&lt;/h3&gt;&lt;p&gt;ByteBuf 是一个字节容器，内部是一个字节数组。从逻辑上来分，字节容器内部，可以分为四个部分：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/013-fb19e073.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一个部分是已经丢弃的字节，这部分数据是无效的；&lt;/li&gt;
&lt;li&gt;第二部分是可读字节，这部分数据是 ByteBuf 的主体数据， 从 ByteBuf 里面读取的数据都来自这一部分；&lt;/li&gt;
&lt;li&gt;第三部分的数据是可写字节，所有写到 ByteBuf 的数据都会写到这一段。&lt;/li&gt;
&lt;li&gt;第四部分的字节，表示的是该 ByteBuf 最多还能扩容的大小。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;四个部分的逻辑功能，如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/014-baeab11b.png"&gt;&lt;/p&gt;
&lt;p&gt;ByteBuf 通过三个整型的指针（index），有效地区分可读数据和可写数据，使得读写之间相互没有冲突。&lt;/p&gt;
&lt;p&gt;这三个指针，分别是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;readerIndex（读指针）&lt;/li&gt;
&lt;li&gt;writerIndex（写指针）&lt;/li&gt;
&lt;li&gt;maxCapacity（最大容量）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/015-a578c322.png"&gt;&lt;/p&gt;
&lt;p&gt;这三个指针，是三个 int 型的成员属性，定义在 AbstractByteBuf 抽象基类中。三个指针的代码截图，如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/016-f2c99b83.png"&gt;&lt;/p&gt;
&lt;p&gt;readerIndex 读指针&lt;/p&gt;
&lt;p&gt;指示读取的起始位置。&lt;/p&gt;
&lt;p&gt;每读取一个字节，readerIndex 自增 1 。一旦 readerIndex 与 writerIndex 相等，ByteBuf 不可读 。&lt;/p&gt;
&lt;p&gt;writerIndex 写指针&lt;/p&gt;
&lt;p&gt;指示写入的起始位置。&lt;/p&gt;
&lt;p&gt;每写一个字节，writerIndex 自增 1。一旦增加到 writerIndex 与 capacity（） 容量相等，表示 ByteBuf 已经不可写了 。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;capacity（）容量不是一个成员属性，是一个成员方法。表示 ByteBuf 内部的总容量。注意，这个不是最大容量。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;maxCapacity 最大容量&lt;/p&gt;
&lt;p&gt;指示可以 ByteBuf 扩容的最大容量。&lt;/p&gt;
&lt;p&gt;当向 ByteBuf 写数据的时候，如果容量不足，可以进行扩容。&lt;/p&gt;
&lt;p&gt;扩容的最大限度，直到 capacity（） 扩容到 maxCapacity 为止，超过 maxCapacity 就会报错。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;capacity() 扩容的操作，是底层自动进行的。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;从三个维度三大系列，介绍 ByteBuf 的常用 API 方法。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/017-f9aef661.png"&gt;&lt;/p&gt;
&lt;p&gt;第一组：容量系列&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方法 一：capacity()&lt;/p&gt;
&lt;p&gt;表示 ByteBuf 的容量，包括丢弃的字节数、可读字节数、可写字节数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法二：maxCapacity()&lt;/p&gt;
&lt;p&gt;表示 ByteBuf 底层最大能够占用的最大字节数。当向 ByteBuf 中写数据的时候，如果发现容量不足，则进行扩容，直到扩容到 maxCapacity。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第二组：写入系列&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方法一：isWritable()&lt;/p&gt;
&lt;p&gt;表示 ByteBuf 是否可写。如果 capacity（） 容量大于 writerIndex 指针的位置 ，则表示可写。否则为不可写。&lt;/p&gt;
&lt;p&gt;isWritable() 的源码，也是很简单的。具体如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;public boolean isWritable() {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; return this.capacity() &amp;gt; this.writerIndex;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;} 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;注意：如果 isWritable() 返回 false，并不代表不能往 ByteBuf 中写数据了。如果 Netty 发现往 ByteBuf 中写数据写不进去的话，会自动扩容 ByteBuf。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方法二：writableBytes()&lt;/p&gt;
&lt;p&gt;返回表示 ByteBuf 当前可写入的字节数，它的值等于 capacity（）- writerIndex。&lt;/p&gt;
&lt;p&gt;如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/018-9671f438.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法三：maxWritableBytes()&lt;/p&gt;
&lt;p&gt;返回可写的最大字节数，它的值等于 maxCapacity-writerIndex 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法四：writeBytes(byte[] src)&lt;/p&gt;
&lt;p&gt;把字节数组 src 里面的数据全部写到 ByteBuf。&lt;/p&gt;
&lt;p&gt;这个是最为常用的一个方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法五：writeTYPE(TYPE value） 基础类型写入方法&lt;/p&gt;
&lt;p&gt;基础数据类型的写入，包含了 8 大基础类型的写入。&lt;/p&gt;
&lt;p&gt;具体如下：writeByte()、 writeBoolean()、writeChar()、writeShort()、writeInt()、writeLong()、writeFloat()、writeDouble() ，向 ByteBuf 写入基础类型的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法六：setType(TYPE value）基础类型写入，不改变指针值&lt;/p&gt;
&lt;p&gt;基础数据类型的写入，包含了 8 大基础类型的写入。&lt;/p&gt;
&lt;p&gt;具体如下：setByte()、 setBoolean()、setChar()、setShort()、setInt()、setLong()、setFloat()、setDouble() ，向 ByteBuf 写入基础类型的数据。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;setType 系列与 writeType 系列的不同：&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;setType 系列 不会 改变写指针 writerIndex ；&lt;/p&gt;
&lt;p&gt;writeTYPE 系列 会 改变写指针 writerIndex 的值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法七：markWriterIndex() 与 resetWriterIndex()&lt;/p&gt;
&lt;p&gt;这里两个方法一起介绍。&lt;/p&gt;
&lt;p&gt;前一个方法，表示把当前的写指针 writerIndex 保存在 markedWriterIndex 属性中 后一个方法，表示把当前的写指针 writerIndex 恢复到之前保存的 markedWriterIndex 值 。&lt;/p&gt;
&lt;p&gt;标记 markedWriterIndex 属性， 定义在 AbstractByteBuf 抽象基类中。&lt;/p&gt;
&lt;p&gt;截图如下：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/019-1920a09a.png"&gt;&lt;/p&gt;
&lt;p&gt;第三组：读取系列&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方法一：isReadable()&lt;/p&gt;
&lt;p&gt;表示 ByteBuf 是否可读。如果 writerIndex 指针的值大于 readerIndex 指针的值 ，则表示可读。否则为不可写。&lt;/p&gt;
&lt;p&gt;isReadable() 的源码，也是很简单的。具体如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;public boolean isReadable() {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; return this.writerIndex &amp;gt; this.readerIndex;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;} 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法二：readableBytes()&lt;/p&gt;
&lt;p&gt;返回表示 ByteBuf 当前可读取的字节数，它的值等于 writerIndex - readerIndex 。&lt;/p&gt;
&lt;p&gt;如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/020-43b20a10.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法三：readBytes(byte[] dst)&lt;/p&gt;
&lt;p&gt;把 ByteBuf 里面的数据全部读取到 dst 字节数组中，这里 dst 字节数组的大小通常等于 readableBytes() 。这个方法，也是最为常用的一个方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法四：readType(） 基础类型读取&lt;/p&gt;
&lt;p&gt;基础数据类型的读取，可以读取 8 大基础类型。&lt;/p&gt;
&lt;p&gt;具体如下：readByte()、readBoolean()、readChar()、readShort()、readInt()、readLong()、readFloat()、readDouble() ，从 ByteBuf 读取对应的基础类型的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法五：getTYPE(TYPE value）基础类型读取，不改变指针值&lt;/p&gt;
&lt;p&gt;基础数据类型的读取，可以读取 8 大基础类型。&lt;/p&gt;
&lt;p&gt;具体如下：getByte()、 getBoolean()、getChar()、getShort()、getInt()、getLong()、getFloat()、getDouble() ，从 ByteBuf 读取对应的基础类型的数据。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;getType 系列与 readTYPE 系列的不同：&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;getType 系列 不会 改变读指针 readerIndex ；&lt;/p&gt;
&lt;p&gt;readTYPE 系列 会 改变读指针 readerIndex 的值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法六：markReaderIndex() 与 resetReaderIndex()&lt;/p&gt;
&lt;p&gt;前一个方法，表示把当前的读指针 ReaderIndex 保存在 markedReaderIndex 属性中。&lt;/p&gt;
&lt;p&gt;后一个方法，表示把当前的读指针 ReaderIndex 恢复到之前保存的 markedReaderIndex 值 。&lt;/p&gt;
&lt;p&gt;标记 markedReaderIndex 属性， 定义在 AbstractByteBuf 抽象基类中。&lt;/p&gt;
&lt;p&gt;截图如下：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/021-21dea289.png"&gt;&lt;/p&gt;
&lt;h3 id="pipeline"&gt;&lt;a href="#pipeline" class="header-anchor"&gt;&lt;/a&gt;pipeline
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;ChannelPipeline 和 ChannelHandler 机制类似于 servlet 和 Filter 过滤器，这类拦截器实际上是职责链模式的一种变形，主要为了方便事件的拦截和用户业务逻辑的定制。&lt;/li&gt;
&lt;li&gt;ChannelPipeline 实际上是一个 ChannelHandler 的容器，内部维护了一个 ChnnelHandler 的链表和迭代器，可以方便地实现 ChannelHandler 查找、添加、替换和删除。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-03-21-zai-liao-liao-netty/022-b7014433.jpg"&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://pdai.tech/md/java/io/java-io-aio.html" target="_blank" rel="noopener"
 &gt;https://pdai.tech/md/java/io/java-io-aio.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247484273&amp;amp;idx=1&amp;amp;sn=a63b992284766a920e0c15ed821e5b02&amp;amp;chksm=eb6dbcf7dc1a35e176706dc3b54f3e4e6fbd53c552868e0162fe8c693ac9b42255d591c5d38a&amp;amp;token=586356569&amp;amp;lang=zh_CN&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;https://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247484273&amp;amp;idx=1&amp;amp;sn=a63b992284766a920e0c15ed821e5b02&amp;amp;chksm=eb6dbcf7dc1a35e176706dc3b54f3e4e6fbd53c552868e0162fe8c693ac9b42255d591c5d38a&amp;amp;token=586356569&amp;amp;lang=zh_CN#rd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://yuanjava.cn/archives/-zhong-bang-tui-jian--yi-wen-pou-xi-reactormo-xing" target="_blank" rel="noopener"
 &gt;http://yuanjava.cn/archives/-zhong-bang-tui-jian--yi-wen-pou-xi-reactormo-xing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/" target="_blank" rel="noopener"
 &gt;https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.artima.com/articles/comparing-two-high-performance-io-design-patterns" target="_blank" rel="noopener"
 &gt;https://www.artima.com/articles/comparing-two-high-performance-io-design-patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://lse.sourceforge.net/io/aio.html" target="_blank" rel="noopener"
 &gt;http://lse.sourceforge.net/io/aio.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://blogxin.cn/2017/03/20/Netty-epollbug/" target="_blank" rel="noopener"
 &gt;http://blogxin.cn/2017/03/20/Netty-epollbug/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://edgar615.github.io/netty-architecture.html" target="_blank" rel="noopener"
 &gt;https://edgar615.github.io/netty-architecture.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://cloud.tencent.com/developer/article/1754078" target="_blank" rel="noopener"
 &gt;https://cloud.tencent.com/developer/article/1754078&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cnblogs.com/rickiyang/p/12904552.html" target="_blank" rel="noopener"
 &gt;https://www.cnblogs.com/rickiyang/p/12904552.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cnblogs.com/crazymakercircle/p/9979897.html" target="_blank" rel="noopener"
 &gt;https://www.cnblogs.com/crazymakercircle/p/9979897.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.lianglianglee.com/" target="_blank" rel="noopener"
 &gt;https://learn.lianglianglee.com/&lt;/a&gt;专栏/Netty 核心原理剖析与 RPC 实践-完/16 IO 加速：与众不同的 Netty 零拷贝技术.md&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>一个学习设计模式的好资源</title><link>https://xiaobox.github.io/p/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/</link><pubDate>Mon, 06 Apr 2020 04:15:27 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/cover.jpg" alt="Featured image of post 一个学习设计模式的好资源" /&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/001-215666a3.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://refactoringguru.cn/design-patterns" target="_blank" rel="noopener"
 &gt;https://refactoringguru.cn/design-patterns&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上面是网站地址，不说别的，光是图画的就深得我心。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/002-bb269594.png"&gt;&lt;/p&gt;
&lt;p&gt;贴几个大家感受下：&lt;/p&gt;
&lt;p&gt;工厂方法模式是一种创建型设计模式， 其在父类中提供一个创建对象的接口， 允许子类决定实例化对象的类型。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/003-c9bdaf9c.png"&gt;&lt;/p&gt;
&lt;p&gt;抽象工厂模式是一种创建型设计模式， 它能创建一系列相关的对象， 而无需指定其具体类。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/004-2ca0bc14.png"&gt;&lt;/p&gt;
&lt;p&gt;生成器模式是一种创建型设计模式， 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/005-7594ad1e.png"&gt;&lt;/p&gt;
&lt;p&gt;原型模式是一种创建型设计模式， 使你能够复制已有对象， 而又无需使代码依赖它们所属的类。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/006-a4b60b5a.png"&gt;&lt;/p&gt;
&lt;p&gt;单例模式是一种创建型设计模式， 让你能够保证一个类只有一个实例， 并提供一个访问该实例的全局节点。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/007-d424dc2e.png"&gt;&lt;/p&gt;
&lt;p&gt;适配器模式是一种结构型设计模式， 它能使接口不兼容的对象能够相互合作。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/008-b67bab67.png"&gt;&lt;/p&gt;
&lt;p&gt;怎么样？还不错吧，有些模式光看图你就大概明白是怎么回事儿了。网站上也有它的电子书卖（注意，这不是广告，我也没拿钱啊&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/009-76280b2b.png"&gt;）。想买书的也可以买本来看看，不过我觉得看网站上的大部分内容应该就够了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-04-06-yi-ge-xue-xi-she-ji-mo-shi-de-hao-zi-yuan/010-84e813ce.jpg"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;</description></item><item><title>一个实例搞懂抽象工厂模式</title><link>https://xiaobox.github.io/p/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/</link><pubDate>Sun, 15 Mar 2020 16:00:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/cover.jpg" alt="Featured image of post 一个实例搞懂抽象工厂模式" /&gt;&lt;p&gt;关于工厂模式三兄弟（简单工厂、工厂方法、抽象工厂），前两个兄弟比较容易懂，基本一看就明白，第三位兄弟初看起来确实有些抽象，网上看到一个例子讲的很好，分享给大家，保证一看就明白。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/001-7131a9a3.png"&gt;&lt;/p&gt;
&lt;p&gt;文末给出系列文章出处。&lt;/p&gt;
&lt;p&gt;Sunny软件公司欲开发一套界面皮肤库，可以对Java桌面软件进行界面美化。为了保护版权，该皮肤库源代码不打算公开，而只向用户提供已打包为jar文件的class字节码文件。用户在使用时可以通过菜单来选择皮肤，不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素，其结构示意图如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/002-f71d2b7c.jpg"&gt;&lt;/p&gt;
&lt;p&gt;图1 界面皮肤库结构示意图&lt;/p&gt;
&lt;p&gt;该皮肤库需要具备良好的灵活性和可扩展性，用户可以自由选择不同的皮肤，开发人员可以在不修改既有代码的基础上增加新的皮肤。&lt;/p&gt;
&lt;p&gt;Sunny软件公司的开发人员针对上述要求，决定使用工厂方法模式进行系统的设计，为了保证系统的灵活性和可扩展性，提供一系列具体工厂来创建按钮、文本框、组合框等界面元素，客户端针对抽象工厂编程，初始结构如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/003-a0a790d3.jpg"&gt;&lt;/p&gt;
&lt;p&gt;图2 基于&lt;strong&gt;工厂方法模式&lt;/strong&gt;的界面皮肤库初始结构图&lt;/p&gt;
&lt;p&gt;在图2中，提供了大量工厂来创建具体的界面组件，可以通过配置文件更换具体界面组件从而改变界面风格。但是，此设计方案存在如下问题：&lt;/p&gt;
&lt;p&gt;(1) 当需要增加新的皮肤时，虽然不要修改现有代码，但是需要增加大量类，针对每一个新增具体组件都需要增加一个具体工厂，类的个数成对增加，这无疑会导致系统越来越庞大，增加系统的维护成本和运行开销；&lt;/p&gt;
&lt;p&gt;(2) 由于同一种风格的具体界面组件通常要一起显示，因此需要为每个组件都选择一个具体工厂，用户在使用时必须逐个进行设置，如果某个具体工厂选择失误将会导致界面显示混乱，虽然我们可以适当增加一些约束语句，但客户端代码和配置文件都较为复杂。&lt;/p&gt;
&lt;p&gt;如何减少系统中类的个数并保证客户端每次始终只使用某一种风格的具体界面组件？这是Sunny公司开发人员所面临的两个问题，显然，工厂方法模式无法解决这两个问题，别着急，本文所介绍的抽象工厂模式可以让这些问题迎刃而解。&lt;/p&gt;
&lt;p&gt;Sunny公司开发人员使用&lt;strong&gt;抽象工厂模式&lt;/strong&gt;来重构界面皮肤库的设计，其基本结构如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/004-9375ae80.jpg"&gt;&lt;/p&gt;
&lt;p&gt;如果需要更换皮肤，只需修改配置文件即可，在实际环境中，我们可以提供可视化界面，例如菜单或者窗口来修改配置文件，用户无须直接修改配置文件。如果需要增加新的皮肤，只需增加一族新的具体组件并对应提供一个新的具体工厂，修改配置文件即可使用新的皮肤，原有代码无须修改，符合“开闭原则”。&lt;/p&gt;
&lt;p&gt;扩展&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;在真实项目开发中，我们通常会为配置文件提供一个可视化的编辑界面，类似Struts框架中的struts.xml编辑器，大家可以自行开发一个简单的图形化工具来修改配置文件，实现真正的纯界面操作。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;本文并没有展开讲抽象工厂模式的方方面面，只是用一个例子，让大家对这个设计模式有个直观的感受，让它“落地”到实例中，建议大家看看下面的系列文章（认真看用不了多久&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/005-7e4a6f2e.png"&gt;），我也是从这里摘取的例子。&lt;/p&gt;
&lt;h1 id="以下为文章参考出处-工厂三兄弟之抽象工厂模式"&gt;&lt;a href="#%e4%bb%a5%e4%b8%8b%e4%b8%ba%e6%96%87%e7%ab%a0%e5%8f%82%e8%80%83%e5%87%ba%e5%a4%84-%e5%b7%a5%e5%8e%82%e4%b8%89%e5%85%84%e5%bc%9f%e4%b9%8b%e6%8a%bd%e8%b1%a1%e5%b7%a5%e5%8e%82%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;以下为文章参考出处: 《工厂三兄弟之抽象工厂模式》
&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89.md" target="_blank" rel="noopener"
 &gt;https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89.md&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89.md" target="_blank" rel="noopener"
 &gt;https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89.md&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%89%EF%BC%89.md" target="_blank" rel="noopener"
 &gt;https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%89%EF%BC%89.md&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%9B%9B%EF%BC%89.md" target="_blank" rel="noopener"
 &gt;https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%9B%9B%EF%BC%89.md&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%94%EF%BC%89.md" target="_blank" rel="noopener"
 &gt;https://github.com/quanke/design-pattern-java/blob/master/%E5%B7%A5%E5%8E%82%E4%B8%89%E5%85%84%E5%BC%9F%E4%B9%8B%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%94%EF%BC%89.md&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-15-yi-ge-shi-li-gao-dong-chou-xiang-gong-chang-mo-shi/006-57ddc455.jpg"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;</description></item><item><title>设计模式没时间学？(一图带你搞定23种设计模式)</title><link>https://xiaobox.github.io/p/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/</link><pubDate>Sat, 14 Mar 2020 16:00:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/cover.jpg" alt="Featured image of post 设计模式没时间学？(一图带你搞定23种设计模式)" /&gt;&lt;p&gt;**设计模式学完容易忘?
**&lt;/p&gt;
&lt;p&gt;**设计新东西时，不知道用哪个？
**&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;知道用哪个模式又开始手忙脚乱的一通查？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;面试让画个设计模式的UML图拉了胯？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你碰到的问题很多人都有&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/001-cbf3152b.png"&gt;&lt;/p&gt;
&lt;p&gt;早在2007年国外的Jason McDonald小哥就为我们整理出了一个超精简版设计模式文件（文末有下载链接 ，大家可以去下载原PDF文件）&lt;/p&gt;
&lt;p&gt;所谓精简版，既没有详细地论证和介绍每一个模式，而是让在你有一定理论基础的情况下，通过这个文件快速回顾或回忆起来。如果你对每一种设计模式还不清楚，建议还是先认认真真学习一遍（比如看四人帮写的相关书籍），不然看了也不深刻。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/002-8a0b2bc1.png"&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/003-0036bf1d.png"&gt;&lt;/p&gt;
&lt;h3 id="设计模式分类"&gt;&lt;a href="#%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e5%88%86%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;设计模式分类
&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;这23种设计模式，可以分为三类，如下图所示
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;蓝色的C就是创建型模式&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;绿色的B就是行为型模式&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;橙色的S就是结构型模式&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/004-fdcfe998.png"&gt;&lt;/p&gt;
&lt;h3 id="责任链模式"&gt;&lt;a href="#%e8%b4%a3%e4%bb%bb%e9%93%be%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;责任链模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/005-645fa31f.png"&gt;&lt;/p&gt;
&lt;h3 id="命令模式"&gt;&lt;a href="#%e5%91%bd%e4%bb%a4%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;命令模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/006-a3db9d0c.png"&gt;&lt;/p&gt;
&lt;h3 id="解释器模式"&gt;&lt;a href="#%e8%a7%a3%e9%87%8a%e5%99%a8%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;解释器模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/007-a6dd68e8.png"&gt;&lt;/p&gt;
&lt;h3 id="迭代器模式"&gt;&lt;a href="#%e8%bf%ad%e4%bb%a3%e5%99%a8%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;迭代器模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/008-24511dc2.png"&gt;&lt;/p&gt;
&lt;h3 id="中介者模式"&gt;&lt;a href="#%e4%b8%ad%e4%bb%8b%e8%80%85%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;中介者模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/009-a62872f9.png"&gt;&lt;/p&gt;
&lt;h3 id="备忘录模式"&gt;&lt;a href="#%e5%a4%87%e5%bf%98%e5%bd%95%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;备忘录模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/010-705aabee.png"&gt;&lt;/p&gt;
&lt;h3 id="观察者模式"&gt;&lt;a href="#%e8%a7%82%e5%af%9f%e8%80%85%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;观察者模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/011-ecb68789.png"&gt;&lt;/p&gt;
&lt;h3 id="状态模式"&gt;&lt;a href="#%e7%8a%b6%e6%80%81%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;状态模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/012-f7865201.png"&gt;&lt;/p&gt;
&lt;h3 id="策略模式"&gt;&lt;a href="#%e7%ad%96%e7%95%a5%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;策略模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/013-ec5c59c6.png"&gt;&lt;/p&gt;
&lt;h3 id="模版模式"&gt;&lt;a href="#%e6%a8%a1%e7%89%88%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;模版模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/014-f16adead.png"&gt;&lt;/p&gt;
&lt;h3 id="访问者模式"&gt;&lt;a href="#%e8%ae%bf%e9%97%ae%e8%80%85%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;访问者模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/015-3c02557a.jpg"&gt;&lt;/p&gt;
&lt;h3 id="适配器模式"&gt;&lt;a href="#%e9%80%82%e9%85%8d%e5%99%a8%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;适配器模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/016-e4230923.png"&gt;&lt;/p&gt;
&lt;h3 id="桥接模式"&gt;&lt;a href="#%e6%a1%a5%e6%8e%a5%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;桥接模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/017-b006237e.png"&gt;&lt;/p&gt;
&lt;h3 id="组合模式"&gt;&lt;a href="#%e7%bb%84%e5%90%88%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;组合模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/018-c0d0ae99.png"&gt;&lt;/p&gt;
&lt;h3 id="装饰器模式"&gt;&lt;a href="#%e8%a3%85%e9%a5%b0%e5%99%a8%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;装饰器模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/019-94fd6c12.png"&gt;&lt;/p&gt;
&lt;h3 id="门面模式"&gt;&lt;a href="#%e9%97%a8%e9%9d%a2%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;门面模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/020-bcc9155e.png"&gt;&lt;/p&gt;
&lt;h3 id="享元模式"&gt;&lt;a href="#%e4%ba%ab%e5%85%83%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;享元模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/021-f40c5e77.png"&gt;&lt;/p&gt;
&lt;h3 id="代理模式"&gt;&lt;a href="#%e4%bb%a3%e7%90%86%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;代理模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/022-7683b2d9.png"&gt;&lt;/p&gt;
&lt;h3 id="抽象工厂模式"&gt;&lt;a href="#%e6%8a%bd%e8%b1%a1%e5%b7%a5%e5%8e%82%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;抽象工厂模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/023-75327bf4.png"&gt;&lt;/p&gt;
&lt;h3 id="构造器模式"&gt;&lt;a href="#%e6%9e%84%e9%80%a0%e5%99%a8%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;构造器模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/024-ba2cc86e.png"&gt;&lt;/p&gt;
&lt;h3 id="工厂方法模式"&gt;&lt;a href="#%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;工厂方法模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/025-62cfcc77.png"&gt;&lt;/p&gt;
&lt;h3 id="原型模式"&gt;&lt;a href="#%e5%8e%9f%e5%9e%8b%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;原型模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/026-16f1022c.png"&gt;&lt;/p&gt;
&lt;h3 id="单例模式"&gt;&lt;a href="#%e5%8d%95%e4%be%8b%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;单例模式
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/027-b88d1fe1.png"&gt;&lt;/p&gt;
&lt;p&gt;完整PDF文件可以通过这个链接下载：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://www.mcdonaldland.info/files/designpatterns/designpatternscard.pdf" target="_blank" rel="noopener"
 &gt;http://www.mcdonaldland.info/files/designpatterns/designpatternscard.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-14-she-ji-mo-shi-mei-shi-jian-xue-yi-tu-dai-ni-gao-ding-23-zhon/028-c0624d98.jpg"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;</description></item><item><title>如何学习算法？</title><link>https://xiaobox.github.io/p/2020-02-15-ru-he-xue-xi-suan-fa/</link><pubDate>Sat, 15 Feb 2020 17:13:17 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-02-15-ru-he-xue-xi-suan-fa/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-15-ru-he-xue-xi-suan-fa/cover.jpg" alt="Featured image of post 如何学习算法？" /&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-15-ru-he-xue-xi-suan-fa/001-4de735a4.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最近读了些文章，以下的文章并没有对具体算法的解答，有的是一些关于学习、信念等问题的更为抽象的思考和方法论，我个人读完认为挺有用的，分享给大家。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我知道很多人对于学习，尤其是算法学习是有一些心理障碍，或者缺乏信念，关于如何保持坚定的信念可以看看这篇：&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzA4NjI3OTY4Mg==&amp;amp;mid=2651821403&amp;amp;idx=2&amp;amp;sn=557622466f7f11aa44cd73380965fd28&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;别高估自己1年的成就，却低估自己10年的发展&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;总结来说：“&lt;strong&gt;认知 信念 原则 执行&lt;/strong&gt;”&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;进入正题&lt;/p&gt;
&lt;p&gt;首先是讲学习方法的：&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzU4NTIxODYwMQ==&amp;amp;mid=2247483836&amp;amp;idx=1&amp;amp;sn=90854aa76507281403e4dd9cd434a12b&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;如果高效学习有什么秘诀的话，那就都在这里了：）&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文中主要强调几点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不要完美主义！&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不要过度“学习路径依赖”，学习要冲着自己的目标去&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不要看不起“薄薄”的“傻”教材，这些你看不起的学习材料，可能是你入门某个领域的关键&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;不要迷信单一教材&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;实践！&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;debug非常非常重要&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;量变到质变&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;最后，一定要相信时间的力量&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接着是探讨学习算法有没有用的问题：&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzU4NTIxODYwMQ==&amp;amp;mid=2247483901&amp;amp;idx=1&amp;amp;sn=c6a6e50354f1abbf2109b1d62bcf5237&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;学算法有什么用？唉，对你来说，可能真没用&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文中重点是：&lt;/p&gt;
&lt;p&gt;“&lt;strong&gt;算法不是技术领域的唯一的核心竞争力，但无论是一个人，一个企业，还是做一份事业，都需要有核心竞争力。什么都没有，肯定是不行的。很多同学问我，去大厂工作，一定要有算法比赛的成绩吗？答案当然不是。我认识太多大佬，没有参加过任何算法比赛，轻轻松松进大厂。有的大佬在面试时直接说：算法我不太懂，但是设计模式软件架构随便问；有的大佬则本科三年就做出一个简易的操作系统内核，面试时聊os把面试官聊晕；有的大佬在iPhone 3的年代就自学iOS开发，一年时间直接进大厂iOS部门当负责人；有的大佬只有高中学历，考不上大学，自学外挂技术竟然成才，如今成为知名游戏厂商的安全部门技术大拿。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;所以，“没有什么”从来不是问题。&lt;strong&gt;&lt;strong&gt;关键问题，从来都是：&lt;/strong&gt;&lt;/strong&gt;“你有什么”。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;怎么才叫学会了？ ：&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzU4NTIxODYwMQ==&amp;amp;mid=2247484021&amp;amp;idx=1&amp;amp;sn=2901f32cbf44739645301d60ae55f926&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;什么叫学会了？自己到底有没有学会？知识掌握的七个境界&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;也许你会说，我不会算法照样能在大厂混，算法非得学吗？关于这个可以看看这篇：&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzU4NTIxODYwMQ==&amp;amp;mid=2247484599&amp;amp;idx=1&amp;amp;sn=5f7bfd01d719e75c663211d5dbfb8cfe&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;大厂面试为什么总考算法？以及如何避开算法面试。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;关于以上的问题，文中已经有答案了：&lt;strong&gt;就是成为领域专家&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最后，祝你成为技术大牛。关于如何成为，可以看看这篇&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-15-ru-he-xue-xi-suan-fa/002-5414ca5a.png"&gt;：&lt;a class="link" href="https://mp.weixin.qq.com/s?__biz=MzU4NTIxODYwMQ==&amp;amp;mid=2247484568&amp;amp;idx=1&amp;amp;sn=d57de93223cdbddb32cb6fff74176929&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;资深技术 Leader 曹乐：如何成为技术大牛&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文中强调：&lt;/p&gt;
&lt;p&gt;“&lt;strong&gt;但其实&lt;/strong&gt;在成为技术大牛的路上，方法反而是没那么重要的。&lt;strong&gt;真正困难的，在于数年，数十年如一日的坚持。太多人遇到挫折，遇到瓶颈，就觉得手头的事情太乏味枯燥，就想要换一个方向，换一个领域，去学新的技术，新的东西。而真正能够成为大牛的，必须是能够青灯古佛，熬得住突破瓶颈前长时间的寂寞的，必须是&lt;/strong&gt;肯下笨功夫的聪明人**。因此**，&lt;strong&gt;和坚持相比，方法其实并没有那么重要。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-15-ru-he-xue-xi-suan-fa/003-42df053f.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关注公众号 获取更多精彩内容&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>