<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>一个旅人</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.codenav.top/</id>
  <link href="https://blog.codenav.top/" rel="alternate"/>
  <link href="https://blog.codenav.top/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 一个旅人</rights>
  <subtitle>程序设计 · 技术分享 · 思考记录</subtitle>
  <title>旅人小站</title>
  <updated>2026-05-27T03:46:05.529Z</updated>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Python" scheme="https://blog.codenav.top/categories/Python/"/>
    <category term="基础知识" scheme="https://blog.codenav.top/tags/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    <category term="Python" scheme="https://blog.codenav.top/tags/Python/"/>
    <content>
      <![CDATA[<h1 id="Python-基础核心总结-🐍"><a href="#Python-基础核心总结-🐍" class="headerlink" title="Python 基础核心总结 🐍"></a>Python 基础核心总结 🐍</h1><blockquote><p>Python 是一门<strong>简洁优雅</strong>、<strong>上手极快</strong>的解释型语言，由 Guido van Rossum 于 1991 年首次发布。它以<strong>可读性极高</strong>的语法、<strong>丰富强大的</strong>标准库、活跃的开源生态闻名于世。本文将系统梳理 Python 基础知识体系，帮助你快速建立起完整的知识框架。🚀</p></blockquote><hr><h2 id="📖-目录"><a href="#📖-目录" class="headerlink" title="📖 目录"></a>📖 目录</h2><ol><li><a href="#python-%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E6%B5%81%E7%A8%8B">Python 程序运行流程</a></li><li><a href="#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B">数据类型</a></li><li><a href="#%E6%8E%A7%E5%88%B6%E6%B5%81%E7%A8%8B">控制流程</a></li><li><a href="#%E5%87%BD%E6%95%B0%E5%9F%BA%E7%A1%80">函数基础</a></li><li><a href="#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%9F%BA%E7%A1%80">面向对象基础</a></li><li><a href="#%E6%A8%A1%E5%9D%97%E4%B8%8E%E5%8C%85">模块与包</a></li><li><a href="#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86">异常处理</a></li><li><a href="#%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C">文件操作</a></li></ol><hr><h2 id="Python-程序运行流程"><a href="#Python-程序运行流程" class="headerlink" title="Python 程序运行流程"></a>Python 程序运行流程</h2><h3 id="解释型语言的特点"><a href="#解释型语言的特点" class="headerlink" title="解释型语言的特点"></a>解释型语言的特点</h3><p>Python 属于<strong>解释型语言</strong>，代码无需提前编译，解释器（Interpreter）会逐行读取源代码，将其转换为<strong>字节码</strong>（.pyc 文件），再由 Python 虚拟机（PVM）执行。这种模式和 Java 的 JVM 有些相似，只是少了显式的编译步骤。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["Python 源代码 (.py)"] --> B["Python 解释器"]    B --> C["字节码 (.pyc)"]    C --> D["Python 虚拟机 PVM"]    D --> E["操作系统"]    E --> F["硬件"]    style A fill:#e1f5ff    style C fill:#fff3e0    style F fill:#e8f5e9</pre></div><h3 id="执行方式一览"><a href="#执行方式一览" class="headerlink" title="执行方式一览"></a>执行方式一览</h3><table><thead><tr><th>方式</th><th>命令</th><th>说明</th></tr></thead><tbody><tr><td><strong>交互式</strong></td><td><code>python</code> &#x2F; <code>python3</code></td><td>每行输入即执行，适合学习和调试</td></tr><tr><td><strong>脚本式</strong></td><td><code>python script.py</code></td><td>将代码写入文件后一次性运行</td></tr><tr><td><strong>编译字节码</strong></td><td><code>python -m py_compile script.py</code></td><td>预编译生成 .pyc 文件，加快导入速度</td></tr></tbody></table><blockquote><p>💡 小贴士：Python 3 的解释器命令通常用 <code>python3</code>，部分系统上 <code>python</code> 默认指向 Python 2。</p></blockquote><hr><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><h3 id="基本数据类型"><a href="#基本数据类型" class="headerlink" title="基本数据类型"></a>基本数据类型</h3><p>Python 内置了丰富的基本数据类型，无需额外声明，直接赋值即可：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 整数</span></span><br><span class="line">age = <span class="number">25</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 浮点数</span></span><br><span class="line">height = <span class="number">1.75</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 字符串</span></span><br><span class="line">name = <span class="string">&quot;Alice&quot;</span></span><br><span class="line">message = <span class="string">&#x27;Hello, Python!&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 布尔值</span></span><br><span class="line">is_student = <span class="literal">True</span></span><br><span class="line">is_empty = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 空值</span></span><br><span class="line">result = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看数据类型</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(age))        <span class="comment"># &lt;class &#x27;int&#x27;&gt;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(height))     <span class="comment"># &lt;class &#x27;float&#x27;&gt;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(name))       <span class="comment"># &lt;class &#x27;str&#x27;&gt;</span></span><br></pre></td></tr></table></figure><h3 id="常用容器类型"><a href="#常用容器类型" class="headerlink" title="常用容器类型"></a>常用容器类型</h3><h4 id="📋-列表（List）"><a href="#📋-列表（List）" class="headerlink" title="📋 列表（List）"></a>📋 列表（List）</h4><p>列表是 Python 最常用的<strong>有序可变容器</strong>，类似其他语言的数组，但功能强大得多：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建列表</span></span><br><span class="line">fruits = [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;orange&quot;</span>]</span><br><span class="line">numbers = <span class="built_in">list</span>(<span class="built_in">range</span>(<span class="number">1</span>, <span class="number">6</span>))  <span class="comment"># [1, 2, 3, 4, 5]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加元素</span></span><br><span class="line">fruits.append(<span class="string">&quot;grape&quot;</span>)       <span class="comment"># 末尾添加</span></span><br><span class="line">fruits.insert(<span class="number">0</span>, <span class="string">&quot;mango&quot;</span>)    <span class="comment"># 指定位置插入</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除元素</span></span><br><span class="line">fruits.remove(<span class="string">&quot;banana&quot;</span>)      <span class="comment"># 按值删除</span></span><br><span class="line">popped = fruits.pop()        <span class="comment"># 弹出末尾元素</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 切片</span></span><br><span class="line"><span class="built_in">print</span>(fruits[<span class="number">0</span>])             <span class="comment"># 第一个元素</span></span><br><span class="line"><span class="built_in">print</span>(fruits[-<span class="number">1</span>])            <span class="comment"># 最后一个元素</span></span><br><span class="line"><span class="built_in">print</span>(fruits[<span class="number">1</span>:<span class="number">3</span>])           <span class="comment"># 切片：索引1到2</span></span><br><span class="line"><span class="built_in">print</span>(fruits[::<span class="number">2</span>])           <span class="comment"># 步长2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 列表推导式</span></span><br><span class="line">squares = [x**<span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line">evens = [x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">20</span>) <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span>]</span><br></pre></td></tr></table></figure><h4 id="🔒-元组（Tuple）"><a href="#🔒-元组（Tuple）" class="headerlink" title="🔒 元组（Tuple）"></a>🔒 元组（Tuple）</h4><p>元组与列表类似，但<strong>不可变</strong>，适合存储固定数据：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建元组</span></span><br><span class="line">point = (<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">colors = (<span class="string">&quot;red&quot;</span>, <span class="string">&quot;green&quot;</span>, <span class="string">&quot;blue&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 单元素元组（必须加逗号）</span></span><br><span class="line">single = (<span class="number">42</span>,)  </span><br><span class="line"></span><br><span class="line"><span class="comment"># 解包</span></span><br><span class="line">x, y = point</span><br><span class="line">a, b, c = colors</span><br><span class="line"></span><br><span class="line"><span class="comment"># 常用方法</span></span><br><span class="line"><span class="built_in">print</span>(colors.index(<span class="string">&quot;red&quot;</span>))   <span class="comment"># 0</span></span><br><span class="line"><span class="built_in">print</span>(colors.count(<span class="string">&quot;blue&quot;</span>)) <span class="comment"># 1</span></span><br></pre></td></tr></table></figure><h4 id="🔑-集合（Set）"><a href="#🔑-集合（Set）" class="headerlink" title="🔑 集合（Set）"></a>🔑 集合（Set）</h4><p>集合用于存储<strong>无序唯一</strong>的元素，自动去重：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建集合</span></span><br><span class="line">skills = &#123;<span class="string">&quot;Python&quot;</span>, <span class="string">&quot;Java&quot;</span>, <span class="string">&quot;Go&quot;</span>, <span class="string">&quot;Python&quot;</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(skills)  <span class="comment"># &#123;&#x27;Python&#x27;, &#x27;Java&#x27;, &#x27;Go&#x27;&#125;  自动去重</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 集合运算</span></span><br><span class="line">set1 = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;</span><br><span class="line">set2 = &#123;<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>&#125;</span><br><span class="line"><span class="built_in">print</span>(set1 &amp; set2)  <span class="comment"># &#123;3, 4&#125; 交集</span></span><br><span class="line"><span class="built_in">print</span>(set1 | set2)  <span class="comment"># &#123;1, 2, 3, 4, 5, 6&#125; 并集</span></span><br><span class="line"><span class="built_in">print</span>(set1 - set2)  <span class="comment"># &#123;1, 2&#125; 差集</span></span><br><span class="line"><span class="built_in">print</span>(set1 ^ set2)  <span class="comment"># &#123;1, 2, 5, 6&#125; 对称差集</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加 / 删除</span></span><br><span class="line">skills.add(<span class="string">&quot;Rust&quot;</span>)</span><br><span class="line">skills.discard(<span class="string">&quot;Java&quot;</span>)   <span class="comment"># 安全删除，不存在不报错</span></span><br><span class="line">skills.remove(<span class="string">&quot;Go&quot;</span>)      <span class="comment"># 删除，不存在抛异常</span></span><br></pre></td></tr></table></figure><h4 id="📖-字典（Dict）"><a href="#📖-字典（Dict）" class="headerlink" title="📖 字典（Dict）"></a>📖 字典（Dict）</h4><p>字典是<strong>键值对（Key-Value）</strong>的无序容器，查找效率接近 O(1)：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建字典</span></span><br><span class="line">person = &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>,</span><br><span class="line">    <span class="string">&quot;age&quot;</span>: <span class="number">25</span>,</span><br><span class="line">    <span class="string">&quot;city&quot;</span>: <span class="string">&quot;Shanghai&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 访问</span></span><br><span class="line"><span class="built_in">print</span>(person[<span class="string">&quot;name&quot;</span>])          <span class="comment"># Alice</span></span><br><span class="line"><span class="built_in">print</span>(person.get(<span class="string">&quot;age&quot;</span>, <span class="number">0</span>))    <span class="comment"># 25，默认值</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加 / 修改</span></span><br><span class="line">person[<span class="string">&quot;email&quot;</span>] = <span class="string">&quot;alice@example.com&quot;</span></span><br><span class="line">person[<span class="string">&quot;age&quot;</span>] = <span class="number">26</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除</span></span><br><span class="line"><span class="keyword">del</span> person[<span class="string">&quot;city&quot;</span>]</span><br><span class="line">popped = person.pop(<span class="string">&quot;email&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历</span></span><br><span class="line"><span class="keyword">for</span> key, value <span class="keyword">in</span> person.items():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;key&#125;</span>: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> key <span class="keyword">in</span> person.keys():</span><br><span class="line">    <span class="built_in">print</span>(key)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> value <span class="keyword">in</span> person.values():</span><br><span class="line">    <span class="built_in">print</span>(value)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 字典推导式</span></span><br><span class="line">squares_dict = &#123;x: x**<span class="number">2</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>)&#125;</span><br></pre></td></tr></table></figure><h3 id="数据类型全家福"><a href="#数据类型全家福" class="headerlink" title="数据类型全家福"></a>数据类型全家福</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["Python 数据类型"] --> B["基本数据类型"]    A --> C["容器类型"]    B --> B1["int 整数"]    B --> B2["float 浮点数"]    B --> B3["str 字符串"]    B --> B4["bool 布尔值"]    B --> B5["None 空值"]    C --> C1["list 列表"]    C --> C2["tuple 元组"]    C --> C3["set 集合"]    C --> C4["dict 字典"]    C1 --> C1a["可变的 ordered"]    C2 --> C2a["不可变的 ordered"]    C3 --> C3a["可变的 unordered 唯一"]    C4 --> C4a["可变的 unordered K-V"]    style B fill:#e3f2fd    style C fill:#f3e5f5</pre></div><hr><h2 id="控制流程"><a href="#控制流程" class="headerlink" title="控制流程"></a>控制流程</h2><h3 id="条件判断"><a href="#条件判断" class="headerlink" title="条件判断"></a>条件判断</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">score = <span class="number">85</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> score &gt;= <span class="number">90</span>:</span><br><span class="line">    grade = <span class="string">&quot;A&quot;</span></span><br><span class="line"><span class="keyword">elif</span> score &gt;= <span class="number">80</span>:</span><br><span class="line">    grade = <span class="string">&quot;B&quot;</span></span><br><span class="line"><span class="keyword">elif</span> score &gt;= <span class="number">60</span>:</span><br><span class="line">    grade = <span class="string">&quot;C&quot;</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    grade = <span class="string">&quot;D&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;成绩等级: <span class="subst">&#123;grade&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 三元表达式</span></span><br><span class="line">result = <span class="string">&quot;及格&quot;</span> <span class="keyword">if</span> score &gt;= <span class="number">60</span> <span class="keyword">else</span> <span class="string">&quot;不及格&quot;</span></span><br></pre></td></tr></table></figure><h3 id="循环结构"><a href="#循环结构" class="headerlink" title="循环结构"></a>循环结构</h3><h4 id="🔄-for-循环"><a href="#🔄-for-循环" class="headerlink" title="🔄 for 循环"></a>🔄 for 循环</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基本语法</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">    <span class="built_in">print</span>(i)  <span class="comment"># 0 1 2 3 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历列表</span></span><br><span class="line">animals = [<span class="string">&quot;cat&quot;</span>, <span class="string">&quot;dog&quot;</span>, <span class="string">&quot;bird&quot;</span>]</span><br><span class="line"><span class="keyword">for</span> animal <span class="keyword">in</span> animals:</span><br><span class="line">    <span class="built_in">print</span>(animal)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 带索引</span></span><br><span class="line"><span class="keyword">for</span> index, animal <span class="keyword">in</span> <span class="built_in">enumerate</span>(animals):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;index&#125;</span>: <span class="subst">&#123;animal&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历字典</span></span><br><span class="line">user = &#123;<span class="string">&quot;name&quot;</span>: <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;age&quot;</span>: <span class="number">30</span>&#125;</span><br><span class="line"><span class="keyword">for</span> key, value <span class="keyword">in</span> user.items():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;key&#125;</span> = <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="🔁-while-循环"><a href="#🔁-while-循环" class="headerlink" title="🔁 while 循环"></a>🔁 while 循环</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">count = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> count &lt; <span class="number">5</span>:</span><br><span class="line">    <span class="built_in">print</span>(count)</span><br><span class="line">    count += <span class="number">1</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;循环正常结束&quot;</span>)  <span class="comment"># else 在循环正常结束时执行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 无限循环 + break</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    response = <span class="built_in">input</span>(<span class="string">&quot;输入 &#x27;quit&#x27; 退出: &quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> response == <span class="string">&quot;quit&quot;</span>:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;你输入了: <span class="subst">&#123;response&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="控制跳转"><a href="#控制跳转" class="headerlink" title="控制跳转"></a>控制跳转</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># break: 跳出当前循环</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">    <span class="keyword">if</span> i == <span class="number">5</span>:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    <span class="built_in">print</span>(i)  <span class="comment"># 0 1 2 3 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># continue: 跳过当前迭代</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">    <span class="keyword">if</span> i == <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    <span class="built_in">print</span>(i)  <span class="comment"># 0 1 3 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># pass: 占位符，不做任何操作</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">    <span class="keyword">if</span> i == <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">pass</span>  <span class="comment"># <span class="doctag">TODO:</span> 稍后实现</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(i)</span><br></pre></td></tr></table></figure><hr><h2 id="函数基础"><a href="#函数基础" class="headerlink" title="函数基础"></a>函数基础</h2><h3 id="函数定义与调用"><a href="#函数定义与调用" class="headerlink" title="函数定义与调用"></a>函数定义与调用</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基本函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;问候函数，返回问候语&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;Hello, <span class="subst">&#123;name&#125;</span>!&quot;</span></span><br><span class="line"></span><br><span class="line">message = greet(<span class="string">&quot;Alice&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(message)  <span class="comment"># Hello, Alice!</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 默认参数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">power</span>(<span class="params">base, exponent=<span class="number">2</span></span>):</span><br><span class="line">    <span class="keyword">return</span> base ** exponent</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(power(<span class="number">3</span>))      <span class="comment"># 9，使用默认值</span></span><br><span class="line"><span class="built_in">print</span>(power(<span class="number">2</span>, <span class="number">3</span>))  <span class="comment"># 8，显式指定</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 可变参数 *args</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sum_all</span>(<span class="params">*numbers</span>):</span><br><span class="line">    total = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> n <span class="keyword">in</span> numbers:</span><br><span class="line">        total += n</span><br><span class="line">    <span class="keyword">return</span> total</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(sum_all(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>))  <span class="comment"># 15</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 关键字参数 **kwargs</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">print_info</span>(<span class="params">**info</span>):</span><br><span class="line">    <span class="keyword">for</span> key, value <span class="keyword">in</span> info.items():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;key&#125;</span>: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">print_info(name=<span class="string">&quot;Bob&quot;</span>, age=<span class="number">30</span>, city=<span class="string">&quot;Beijing&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="函数返回值"><a href="#函数返回值" class="headerlink" title="函数返回值"></a>函数返回值</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 无返回值（默认返回 None）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">print_hello</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello!&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 单返回值</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">square</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> x * x</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多返回值（返回元组）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">divide</span>(<span class="params">a, b</span>):</span><br><span class="line">    quotient = a // b</span><br><span class="line">    remainder = a % b</span><br><span class="line">    <span class="keyword">return</span> quotient, remainder</span><br><span class="line"></span><br><span class="line">q, r = divide(<span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;商=<span class="subst">&#123;q&#125;</span>, 余数=<span class="subst">&#123;r&#125;</span>&quot;</span>)  <span class="comment"># 商=3, 余数=1</span></span><br></pre></td></tr></table></figure><h3 id="特殊函数特性"><a href="#特殊函数特性" class="headerlink" title="特殊函数特性"></a>特殊函数特性</h3><h4 id="Lambda-表达式"><a href="#Lambda-表达式" class="headerlink" title="Lambda 表达式"></a>Lambda 表达式</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># lambda: 匿名函数，适合简短函数</span></span><br><span class="line">square = <span class="keyword">lambda</span> x: x ** <span class="number">2</span></span><br><span class="line"><span class="built_in">print</span>(square(<span class="number">5</span>))  <span class="comment"># 25</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配合内置函数使用</span></span><br><span class="line">numbers = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line">filtered = <span class="built_in">list</span>(<span class="built_in">filter</span>(<span class="keyword">lambda</span> x: x % <span class="number">2</span> == <span class="number">0</span>, numbers))</span><br><span class="line"><span class="built_in">print</span>(filtered)  <span class="comment"># [2, 4]</span></span><br><span class="line"></span><br><span class="line">mapped = <span class="built_in">list</span>(<span class="built_in">map</span>(<span class="keyword">lambda</span> x: x * <span class="number">2</span>, numbers))</span><br><span class="line"><span class="built_in">print</span>(mapped)  <span class="comment"># [2, 4, 6, 8, 10]</span></span><br><span class="line"></span><br><span class="line">sorted_list = <span class="built_in">sorted</span>(numbers, key=<span class="keyword">lambda</span> x: -x)  <span class="comment"># 降序排列</span></span><br><span class="line"><span class="built_in">print</span>(sorted_list)  <span class="comment"># [5, 4, 3, 2, 1]</span></span><br></pre></td></tr></table></figure><h4 id="装饰器"><a href="#装饰器" class="headerlink" title="装饰器"></a>装饰器</h4><p>装饰器可以在不修改原函数的前提下，为函数添加额外功能：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timer</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计时装饰器&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.time()</span><br><span class="line">        result = func(*args, **kwargs)</span><br><span class="line">        end = time.time()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 执行耗时: <span class="subst">&#123;end - start:<span class="number">.4</span>f&#125;</span>秒&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@timer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_function</span>():</span><br><span class="line">    time.sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;Done&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(slow_function())</span><br><span class="line"><span class="comment"># slow_function 执行耗时: 1.0012秒</span></span><br><span class="line"><span class="comment"># Done</span></span><br></pre></td></tr></table></figure><h3 id="函数调用流程"><a href="#函数调用流程" class="headerlink" title="函数调用流程"></a>函数调用流程</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["调用函数"] --> B["传递实参"]    B --> C["绑定形参"]    C --> D["创建局部作用域"]    D --> E["执行函数体"]    E --> F{"有 return？"}    F -->|"是"--> G["返回值"]    F -->|"否"--> H["返回 None"]    G --> I["销毁局部作用域"]    H --> I    I --> J["回到调用点"]    style G fill:#c8e6c9    style H fill:#ffecb3</pre></div><hr><h2 id="面向对象基础"><a href="#面向对象基础" class="headerlink" title="面向对象基础"></a>面向对象基础</h2><h3 id="类与对象"><a href="#类与对象" class="headerlink" title="类与对象"></a>类与对象</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;狗类&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 类属性（所有实例共享）</span></span><br><span class="line">    species = <span class="string">&quot;犬科&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, age</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;初始化方法&quot;&quot;&quot;</span></span><br><span class="line">        <span class="variable language_">self</span>.name = name    <span class="comment"># 实例属性</span></span><br><span class="line">        <span class="variable language_">self</span>.age = age</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">bark</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;叫声方法&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 汪汪叫！&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_info</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span>, <span class="subst">&#123;self.age&#125;</span>岁&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;字符串表示&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;Dog(<span class="subst">&#123;self.name&#125;</span>)&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建实例</span></span><br><span class="line">dog1 = Dog(<span class="string">&quot;旺财&quot;</span>, <span class="number">3</span>)</span><br><span class="line">dog2 = Dog(<span class="string">&quot;小白&quot;</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(dog1.bark())           <span class="comment"># 旺财 汪汪叫！</span></span><br><span class="line"><span class="built_in">print</span>(dog2.get_info())       <span class="comment"># 小白, 1岁</span></span><br><span class="line"><span class="built_in">print</span>(Dog.species)           <span class="comment"># 犬科</span></span><br><span class="line"><span class="built_in">print</span>(dog1)                  <span class="comment"># Dog(旺财)</span></span><br></pre></td></tr></table></figure><h3 id="继承与多态"><a href="#继承与多态" class="headerlink" title="继承与多态"></a>继承与多态</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line">        <span class="variable language_">self</span>.name = name</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError(<span class="string">&quot;子类必须实现此方法&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cat</span>(<span class="title class_ inherited__">Animal</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 喵~&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span>(<span class="title class_ inherited__">Animal</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">speak</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;<span class="subst">&#123;self.name&#125;</span> 汪！&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多态：同一接口，不同实现</span></span><br><span class="line">animals = [Cat(<span class="string">&quot;咪咪&quot;</span>), Dog(<span class="string">&quot;旺财&quot;</span>)]</span><br><span class="line"><span class="keyword">for</span> animal <span class="keyword">in</span> animals:</span><br><span class="line">    <span class="built_in">print</span>(animal.speak())</span><br><span class="line"><span class="comment"># 咪咪 喵~</span></span><br><span class="line"><span class="comment"># 旺财 汪！</span></span><br></pre></td></tr></table></figure><h3 id="访问控制"><a href="#访问控制" class="headerlink" title="访问控制"></a>访问控制</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, username, password</span>):</span><br><span class="line">        <span class="variable language_">self</span>.username = username        <span class="comment"># 公有属性</span></span><br><span class="line">        <span class="variable language_">self</span>.__password = password      <span class="comment"># 私有属性（名称重整）</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_password</span>(<span class="params">self, pwd</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.__password == pwd</span><br><span class="line">    </span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">password</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;属性访问器，只读&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;******&quot;</span></span><br><span class="line"></span><br><span class="line">user = User(<span class="string">&quot;alice&quot;</span>, <span class="string">&quot;secret123&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(user.username)          <span class="comment"># alice</span></span><br><span class="line"><span class="built_in">print</span>(user.password)          <span class="comment"># ******</span></span><br><span class="line"><span class="built_in">print</span>(user.check_password(<span class="string">&quot;secret123&quot;</span>))  <span class="comment"># True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># print(user.__password)     # AttributeError: 访问不到！</span></span><br></pre></td></tr></table></figure><h3 id="类层次结构"><a href="#类层次结构" class="headerlink" title="类层次结构"></a>类层次结构</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["object 基类"] --> B["Animal 动物类"]    B --> C["Cat 猫类"]    B --> D["Dog 狗类"]    B --> E["Bird 鸟类"]    C --> C1["speak()"]    D --> D1["speak()"]    E --> E1["speak()"]    style A fill:#e8f5e9    style B fill:#c8e6c9    style C fill:#b2dfdb    style D fill:#b2dfdb    style E fill:#b2dfdb</pre></div><hr><h2 id="模块与包"><a href="#模块与包" class="headerlink" title="模块与包"></a>模块与包</h2><h3 id="模块基础"><a href="#模块基础" class="headerlink" title="模块基础"></a>模块基础</h3><p>Python 的模块就是一个 <code>.py</code> 文件。以下是标准库中的常用模块：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 导入整个模块</span></span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="built_in">print</span>(math.pi)           <span class="comment"># 3.141592653589793</span></span><br><span class="line"><span class="built_in">print</span>(math.sqrt(<span class="number">16</span>))     <span class="comment"># 4.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入特定成员</span></span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint, choice</span><br><span class="line"><span class="built_in">print</span>(randint(<span class="number">1</span>, <span class="number">10</span>))    <span class="comment"># 1~10 随机整数</span></span><br><span class="line"><span class="built_in">print</span>(choice([<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;c&quot;</span>]))  <span class="comment"># 随机选一个</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入并起别名</span></span><br><span class="line"><span class="keyword">import</span> datetime <span class="keyword">as</span> dt</span><br><span class="line">now = dt.datetime.now()</span><br><span class="line"><span class="built_in">print</span>(now)               <span class="comment"># 2026-05-27 11:45:00.xxx</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入所有（不推荐）</span></span><br><span class="line"><span class="keyword">from</span> math <span class="keyword">import</span> *</span><br></pre></td></tr></table></figure><h3 id="常用标准库一览"><a href="#常用标准库一览" class="headerlink" title="常用标准库一览"></a>常用标准库一览</h3><table><thead><tr><th>模块</th><th>用途</th><th>示例</th></tr></thead><tbody><tr><td><code>os</code></td><td>操作系统接口</td><td><code>os.getcwd()</code>, <code>os.listdir()</code></td></tr><tr><td><code>sys</code></td><td>系统参数</td><td><code>sys.path</code>, <code>sys.argv</code></td></tr><tr><td><code>datetime</code></td><td>日期时间</td><td><code>datetime.datetime.now()</code></td></tr><tr><td><code>json</code></td><td>JSON 处理</td><td><code>json.dumps()</code>, <code>json.loads()</code></td></tr><tr><td><code>re</code></td><td>正则表达式</td><td><code>re.match()</code>, <code>re.findall()</code></td></tr><tr><td><code>collections</code></td><td>集合容器</td><td><code>Counter</code>, <code>defaultdict</code></td></tr><tr><td><code>itertools</code></td><td>迭代工具</td><td><code>count()</code>, <code>cycle()</code></td></tr></tbody></table><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 实战示例：os 和 pathlib</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当前目录</span></span><br><span class="line">cwd = Path.cwd()</span><br><span class="line"><span class="built_in">print</span>(cwd)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历目录</span></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> cwd.iterdir():</span><br><span class="line">    <span class="built_in">print</span>(item.name)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 路径拼接</span></span><br><span class="line">file_path = cwd / <span class="string">&quot;data&quot;</span> / <span class="string">&quot;example.txt&quot;</span></span><br><span class="line"><span class="built_in">print</span>(file_path.exists())</span><br><span class="line"></span><br><span class="line"><span class="comment"># collections 计数器</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line">words = [<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;apple&quot;</span>, <span class="string">&quot;cherry&quot;</span>, <span class="string">&quot;banana&quot;</span>, <span class="string">&quot;apple&quot;</span>]</span><br><span class="line">counter = Counter(words)</span><br><span class="line"><span class="built_in">print</span>(counter.most_common(<span class="number">2</span>))  <span class="comment"># [(&quot;apple&quot;, 3), (&quot;banana&quot;, 2)]</span></span><br></pre></td></tr></table></figure><h3 id="包的结构"><a href="#包的结构" class="headerlink" title="包的结构"></a>包的结构</h3><p>一个典型的 Python 包结构如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mypackage/</span><br><span class="line">├── __init__.py        # 包初始化文件</span><br><span class="line">├── module1.py         # 模块1</span><br><span class="line">├── module2.py         # 模块2</span><br><span class="line">└── subpackage/</span><br><span class="line">    ├── __init__.py</span><br><span class="line">    └── module3.py</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># __init__.py 中可以指定默认导出</span></span><br><span class="line"><span class="comment"># from .module1 import Something</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用包</span></span><br><span class="line"><span class="comment"># from mypackage import module1</span></span><br><span class="line"><span class="comment"># module1.do_something()</span></span><br></pre></td></tr></table></figure><hr><h2 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h2><h3 id="基本结构"><a href="#基本结构" class="headerlink" title="基本结构"></a>基本结构</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    num = <span class="built_in">int</span>(<span class="built_in">input</span>(<span class="string">&quot;输入一个整数: &quot;</span>))</span><br><span class="line">    result = <span class="number">10</span> / num</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;10 / <span class="subst">&#123;num&#125;</span> = <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> ValueError:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;输入无效，请输入整数！&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> ZeroDivisionError:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;除数不能为零！&quot;</span>)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;未知错误: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;全部执行成功，没有异常&quot;</span>)</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;无论是否出错，都会执行&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="主动抛出异常"><a href="#主动抛出异常" class="headerlink" title="主动抛出异常"></a>主动抛出异常</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">divide</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">if</span> b == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ZeroDivisionError(<span class="string">&quot;除数不能为0&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> a / b</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    result = divide(<span class="number">10</span>, <span class="number">0</span>)</span><br><span class="line"><span class="keyword">except</span> ZeroDivisionError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(e)  <span class="comment"># 除数不能为0</span></span><br></pre></td></tr></table></figure><h3 id="自定义异常"><a href="#自定义异常" class="headerlink" title="自定义异常"></a>自定义异常</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ValidationError</span>(<span class="title class_ inherited__">Exception</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自定义验证异常&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, field, message</span>):</span><br><span class="line">        <span class="variable language_">self</span>.field = field</span><br><span class="line">        <span class="variable language_">self</span>.message = message</span><br><span class="line">        <span class="built_in">super</span>().__init__(<span class="string">f&quot;<span class="subst">&#123;field&#125;</span>: <span class="subst">&#123;message&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">validate_age</span>(<span class="params">age</span>):</span><br><span class="line">    <span class="keyword">if</span> age &lt; <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValidationError(<span class="string">&quot;age&quot;</span>, <span class="string">&quot;年龄不能为负数&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> age &gt; <span class="number">150</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValidationError(<span class="string">&quot;age&quot;</span>, <span class="string">&quot;年龄不合理&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    validate_age(-<span class="number">5</span>)</span><br><span class="line"><span class="keyword">except</span> ValidationError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(e)  <span class="comment"># age: 年龄不能为负数</span></span><br></pre></td></tr></table></figure><h3 id="异常处理流程"><a href="#异常处理流程" class="headerlink" title="异常处理流程"></a>异常处理流程</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["try 块"] --> B{"代码执行"}    B -->|"正常"--> C["else 块"]    B -->|"异常"--> D["except 块匹配"]    C --> E["finally 块"]    D --> E    E --> F["继续执行"]    D --> D1["不匹配则上抛"]    style C fill:#e8f5e9    style D fill:#ffcdd2    style E fill:#fff9c4</pre></div><hr><h2 id="文件操作"><a href="#文件操作" class="headerlink" title="文件操作"></a>文件操作</h2><h3 id="读写文本文件"><a href="#读写文本文件" class="headerlink" title="读写文本文件"></a>读写文本文件</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 读取文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;example.txt&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    content = f.read()          <span class="comment"># 读取全部</span></span><br><span class="line">    <span class="comment"># content = f.readline()   # 读取一行</span></span><br><span class="line">    <span class="comment"># content = f.readlines()  # 读取所有行到列表</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;output.txt&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;第一行内容\n&quot;</span>)</span><br><span class="line">    f.write(<span class="string">&quot;第二行内容\n&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 追加模式</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;log.txt&quot;</span>, <span class="string">&quot;a&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(<span class="string">&quot;新的日志记录\n&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="JSON-文件处理"><a href="#JSON-文件处理" class="headerlink" title="JSON 文件处理"></a>JSON 文件处理</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入 JSON</span></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Alice&quot;</span>,</span><br><span class="line">    <span class="string">&quot;age&quot;</span>: <span class="number">25</span>,</span><br><span class="line">    <span class="string">&quot;skills&quot;</span>: [<span class="string">&quot;Python&quot;</span>, <span class="string">&quot;Java&quot;</span>, <span class="string">&quot;Go&quot;</span>]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;user.json&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(data, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取 JSON</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;user.json&quot;</span>, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    loaded = json.load(f)</span><br><span class="line">    <span class="built_in">print</span>(loaded[<span class="string">&quot;name&quot;</span>])  <span class="comment"># Alice</span></span><br></pre></td></tr></table></figure><h3 id="文件操作安全要点"><a href="#文件操作安全要点" class="headerlink" title="文件操作安全要点"></a>文件操作安全要点</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["打开文件"] --> B{"使用 with？"}    B -->|"是"--> C["自动释放资源"]    B -->|"否"--> D["需手动 close()"]    D --> E["忘记 close 导致资源泄露"]    C --> F["读写操作"]    F --> G{"操作成功？"}    G -->|"是"--> H["正常处理"]    G -->|"否"--> I["捕获异常"]    I --> J["记录日志"]    style C fill:#c8e6c9    style D fill:#ffcdd2    style E fill:#ffcdd2</pre></div><blockquote><p>⚠️ <strong>最佳实践</strong>：始终使用 <code>with open(...) as f:</code> 语句，它可以确保文件在使用完毕后自动关闭，即使中途发生异常也不会遗漏。</p></blockquote><hr><h2 id="总结-🎯"><a href="#总结-🎯" class="headerlink" title="总结 🎯"></a>总结 🎯</h2><p>以上我们梳理了 Python 的核心基础知识：</p><table><thead><tr><th>模块</th><th>核心要点</th></tr></thead><tbody><tr><td><strong>运行流程</strong></td><td>解释执行，.py → 字节码 → PVM</td></tr><tr><td><strong>数据类型</strong></td><td>基本类型 + 四大容器（list&#x2F;tuple&#x2F;set&#x2F;dict）</td></tr><tr><td><strong>控制流程</strong></td><td>if&#x2F;elif&#x2F;else + for&#x2F;while + break&#x2F;continue&#x2F;pass</td></tr><tr><td><strong>函数</strong></td><td>def 定义、默认参数、*args、**kwargs、lambda、装饰器</td></tr><tr><td><strong>面向对象</strong></td><td>类、继承、多态、访问控制（私有属性）</td></tr><tr><td><strong>模块</strong></td><td>import&#x2F;from、常用标准库、pip 安装三方库</td></tr><tr><td><strong>异常处理</strong></td><td>try&#x2F;except&#x2F;else&#x2F;finally、自定义异常</td></tr><tr><td><strong>文件操作</strong></td><td>with open、读写文本、JSON 处理</td></tr></tbody></table><p>Python 的设计哲学是：<strong>简洁优于复杂、明了优于隐晦</strong>。掌握这些基础后，你已经具备了继续深入 Web 开发、数据分析、人工智能等方向的核心能力。🚀</p><blockquote><p>🐍 <em>“Simple is better than complex”</em> —— The Zen of Python</p></blockquote><hr><p><em>如果你有任何问题，欢迎留言交流！</em></p>]]>
    </content>
    <id>https://blog.codenav.top/python-basics/</id>
    <link href="https://blog.codenav.top/python-basics/"/>
    <published>2026-05-27T03:45:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Python-基础核心总结-🐍"><a href="#Python-基础核心总结-🐍" class="headerlink" title="Python 基础核心总结 🐍"></a>Python 基础核心总结 🐍</h1><blockquote>
<p>P]]>
    </summary>
    <title>Python 基础核心总结</title>
    <updated>2026-05-27T03:46:05.529Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="技术教程" scheme="https://blog.codenav.top/categories/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/"/>
    <category term="前端" scheme="https://blog.codenav.top/tags/%E5%89%8D%E7%AB%AF/"/>
    <category term="HTML" scheme="https://blog.codenav.top/tags/HTML/"/>
    <category term="CSS" scheme="https://blog.codenav.top/tags/CSS/"/>
    <category term="JavaScript" scheme="https://blog.codenav.top/tags/JavaScript/"/>
    <category term="Web" scheme="https://blog.codenav.top/tags/Web/"/>
    <content>
      <![CDATA[<h1 id="Web前端基础完全指南-🖥️"><a href="#Web前端基础完全指南-🖥️" class="headerlink" title="Web前端基础完全指南 🖥️"></a>Web前端基础完全指南 🖥️</h1><blockquote><p>前端开发是创建用户直接交互的界面部分，涉及HTML、CSS和JavaScript三大基石。本文将带你从零开始，建立对Web前端的系统性认知。</p></blockquote><hr><h2 id="📚-目录"><a href="#📚-目录" class="headerlink" title="📚 目录"></a>📚 目录</h2><ol><li><a href="#web%E5%89%8D%E7%AB%AF%E6%A6%82%E8%BF%B0">Web前端概述</a></li><li><a href="#html%E7%BD%91%E9%A1%B5%E7%9A%84%E7%BB%93%E6%9E%84%E9%AA%A8%E6%9E%B6">HTML：网页的结构骨架</a></li><li><a href="#css%E7%BD%91%E9%A1%B5%E7%9A%84%E8%A7%86%E8%A7%89%E8%89%BA%E6%9C%AF">CSS：网页的视觉艺术</a></li><li><a href="#javascript%E7%BD%91%E9%A1%B5%E7%9A%84%E7%81%B5%E9%AD%82%E4%BA%A4%E4%BA%92">JavaScript：网页的灵魂交互</a></li><li><a href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86">浏览器工作原理</a></li><li><a href="#%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%85%A5%E9%97%A8">前端工程化入门</a></li><li><a href="#%E5%AD%A6%E4%B9%A0%E8%B7%AF%E5%BE%84%E4%B8%8E%E8%B5%84%E6%BA%90%E6%8E%A8%E8%8D%90">学习路径与资源推荐</a></li></ol><hr><h2 id="🌐-Web前端概述"><a href="#🌐-Web前端概述" class="headerlink" title="🌐 Web前端概述"></a>🌐 Web前端概述</h2><h3 id="什么是Web前端？"><a href="#什么是Web前端？" class="headerlink" title="什么是Web前端？"></a>什么是Web前端？</h3><p>Web前端指用户在浏览器中看到的<strong>网页界面及其交互逻辑</strong>。当你在浏览器中打开一个网页时：</p><ul><li><strong>HTML</strong> 定义了网页的内容和结构 📝</li><li><strong>CSS</strong> 控制着网页的外观和布局 🎨</li><li><strong>JavaScript</strong> 赋予网页交互能力 ⚡</li></ul><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph TD    A["用户浏览器"] --> B["HTML文档"]    A --> C["CSS样式"]    A --> D["JavaScript脚本"]    B --> E["网页结构"]    C --> F["视觉效果"]    D --> G["交互逻辑"]    E --> H["完整网页"]    F --> H    G --> H    style A fill:#f9f,stroke:#333,stroke-width:2px    style H fill:#9f9,stroke:#333,stroke-width:2px</pre></div><h3 id="前端技术生态全景图"><a href="#前端技术生态全景图" class="headerlink" title="前端技术生态全景图"></a>前端技术生态全景图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root["Web前端技术栈"]    HTML      HTML5新特性      语义化标签      可访问性    CSS      CSS3动画      Flexbox布局      Grid布局      响应式设计    JavaScript      ES6+新特性      DOM操作      事件处理      异步编程    框架与库      React      Vue      Angular    工具链      npm/yarn      webpack/vite      Git</pre></div><hr><h2 id="🧱-HTML：网页的结构骨架"><a href="#🧱-HTML：网页的结构骨架" class="headerlink" title="🧱 HTML：网页的结构骨架"></a>🧱 HTML：网页的结构骨架</h2><h3 id="HTML是什么？"><a href="#HTML是什么？" class="headerlink" title="HTML是什么？"></a>HTML是什么？</h3><p>HTML（HyperText Markup Language）不是一门编程语言，而是一种<strong>标记语言</strong>。它使用标签来描述网页的结构和内容。</p><h3 id="基础HTML文档结构"><a href="#基础HTML文档结构" class="headerlink" title="基础HTML文档结构"></a>基础HTML文档结构</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;zh-CN&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;viewport&quot;</span> <span class="attr">content</span>=<span class="string">&quot;width=device-width, initial-scale=1.0&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>我的第一个网页<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">header</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h1</span>&gt;</span>欢迎来到我的网站 🌍<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">nav</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;#home&quot;</span>&gt;</span>首页<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;#about&quot;</span>&gt;</span>关于我们<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;#contact&quot;</span>&gt;</span>联系方式<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">nav</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">main</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">section</span> <span class="attr">id</span>=<span class="string">&quot;home&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">h2</span>&gt;</span>首页内容<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">p</span>&gt;</span>这是一个<span class="tag">&lt;<span class="name">strong</span>&gt;</span>段落<span class="tag">&lt;/<span class="name">strong</span>&gt;</span>示例，包含<span class="tag">&lt;<span class="name">em</span>&gt;</span>强调文本<span class="tag">&lt;/<span class="name">em</span>&gt;</span>。<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">section</span> <span class="attr">id</span>=<span class="string">&quot;about&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">h2</span>&gt;</span>关于我们<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">ul</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">li</span>&gt;</span>🎯 使命：让Web更美好<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">li</span>&gt;</span>💡 愿景：连接世界的每一端<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">li</span>&gt;</span>🚀 价值观：创新、协作、开放<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">footer</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span><span class="symbol">&amp;copy;</span> 2026 前端学习站. All rights reserved.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">footer</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="常用HTML标签一览"><a href="#常用HTML标签一览" class="headerlink" title="常用HTML标签一览"></a>常用HTML标签一览</h3><table><thead><tr><th align="center">标签</th><th align="left">用途</th><th align="left">示例</th></tr></thead><tbody><tr><td align="center"><code>&lt;div&gt;</code></td><td align="left">无语义容器</td><td align="left">页面区块划分</td></tr><tr><td align="center"><code>&lt;header&gt;</code></td><td align="left">头部区域</td><td align="left">导航、logo</td></tr><tr><td align="center"><code>&lt;nav&gt;</code></td><td align="left">导航链接</td><td align="left">菜单导航</td></tr><tr><td align="center"><code>&lt;main&gt;</code></td><td align="left">主内容区</td><td align="left">核心内容</td></tr><tr><td align="center"><code>&lt;section&gt;</code></td><td align="left">章节分区</td><td align="left">内容分块</td></tr><tr><td align="center"><code>&lt;article&gt;</code></td><td align="left">文章内容</td><td align="left">博客帖子</td></tr><tr><td align="center"><code>&lt;aside&gt;</code></td><td align="left">侧边栏</td><td align="left">附加信息</td></tr><tr><td align="center"><code>&lt;footer&gt;</code></td><td align="left">页脚区域</td><td align="left">版权信息</td></tr></tbody></table><h3 id="语义化标签的重要性"><a href="#语义化标签的重要性" class="headerlink" title="语义化标签的重要性"></a>语义化标签的重要性</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    A["非语义化写法"] --> B["&lt;div class='header'&gt;"]    A --> C["&lt;div class='nav'&gt;"]    A --> D["&lt;div class='content'&gt;"]        E["语义化写法"] --> F["&lt;header&gt;"]    E --> G["&lt;nav&gt;"]    E --> H["&lt;main&gt;"]        F --> I["✅ SEO友好"]    G --> I    H --> I        F --> J["✅ 可访问性强"]    G --> J    H --> J        F --> K["✅ 代码可维护性高"]    G --> K    H --> K</pre></div><h3 id="表单元素示例"><a href="#表单元素示例" class="headerlink" title="表单元素示例"></a>表单元素示例</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/submit&quot;</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">fieldset</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">legend</span>&gt;</span>用户注册 📝<span class="tag">&lt;/<span class="name">legend</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">&quot;username&quot;</span>&gt;</span>用户名:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">id</span>=<span class="string">&quot;username&quot;</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> </span></span><br><span class="line"><span class="tag">               <span class="attr">placeholder</span>=<span class="string">&quot;请输入用户名&quot;</span> <span class="attr">required</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">&quot;email&quot;</span>&gt;</span>邮箱:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;email&quot;</span> <span class="attr">id</span>=<span class="string">&quot;email&quot;</span> <span class="attr">name</span>=<span class="string">&quot;email&quot;</span> </span></span><br><span class="line"><span class="tag">               <span class="attr">placeholder</span>=<span class="string">&quot;example@domain.com&quot;</span> <span class="attr">required</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">&quot;password&quot;</span>&gt;</span>密码:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">id</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> </span></span><br><span class="line"><span class="tag">               <span class="attr">minlength</span>=<span class="string">&quot;8&quot;</span> <span class="attr">required</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">&quot;gender&quot;</span>&gt;</span>性别:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;gender&quot;</span> <span class="attr">name</span>=<span class="string">&quot;gender&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;&quot;</span>&gt;</span>请选择<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;male&quot;</span>&gt;</span>男 🧑<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;female&quot;</span>&gt;</span>女 👩<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;other&quot;</span>&gt;</span>其他 🏳️‍🌈<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>注册 ✨<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">fieldset</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="🎨-CSS：网页的视觉艺术"><a href="#🎨-CSS：网页的视觉艺术" class="headerlink" title="🎨 CSS：网页的视觉艺术"></a>🎨 CSS：网页的视觉艺术</h2><h3 id="CSS基础语法"><a href="#CSS基础语法" class="headerlink" title="CSS基础语法"></a>CSS基础语法</h3><p>CSS（Cascading Style Sheets）用于控制网页的<strong>视觉表现</strong>，包括颜色、字体、布局等。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 基本选择器 */</span></span><br><span class="line">selector &#123;</span><br><span class="line">    property: value;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 示例 */</span></span><br><span class="line"><span class="selector-class">.title</span> &#123;</span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">24px</span>;</span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#333</span>;</span><br><span class="line">    <span class="attribute">text-align</span>: center;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="选择器的世界"><a href="#选择器的世界" class="headerlink" title="选择器的世界"></a>选择器的世界</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph TD    A["CSS选择器"] --> B["基础选择器"]    A --> C["组合选择器"]    A --> D["伪类选择器"]    A --> E["属性选择器"]        B --> B1["标签选择器: p"]    B --> B2["类选择器: .class"]    B --> B3["ID选择器: #id"]    B --> B4["通配符: *"]        C --> C1["后代: .parent .child"]    C --> C2["子元素: .parent > .child"]    C --> C3["相邻兄弟: .prev + .next"]    C --> C4["通用兄弟: .prev ~ .siblings"]        D --> D1["悬停: :hover"]    D --> D2["点击: :active"]    D --> D3["获取焦点: :focus"]    D --> D4["首个元素: :first-child"]        E --> E1["属性存在: [attribute]"]    E --> E2["属性值: [type='text']"]    E --> E3["包含值: [class~='btn']"]</pre></div><h3 id="盒模型详解"><a href="#盒模型详解" class="headerlink" title="盒模型详解"></a>盒模型详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    A["盒模型结构"] --> B["Content / 内容区域"]    B --> C["Padding / 内边距"]    C --> D["Border / 边框"]    D --> E["Margin / 外边距"]        style B fill:#ff9,stroke:#333    style C fill:#9f9,stroke:#333    style D fill:#9cf,stroke:#333    style E fill:#f9f,stroke:#333</pre></div><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span> &#123;</span><br><span class="line">    <span class="comment">/* 内容尺寸 */</span></span><br><span class="line">    <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line">    <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/* 内边距 */</span></span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/* 边框 */</span></span><br><span class="line">    <span class="attribute">border</span>: <span class="number">2px</span> solid <span class="number">#3498db</span>;</span><br><span class="line">    <span class="attribute">border-radius</span>: <span class="number">8px</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/* 外边距 */</span></span><br><span class="line">    <span class="attribute">margin</span>: <span class="number">30px</span> auto;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/* 盒模型计算方式 */</span></span><br><span class="line">    <span class="attribute">box-sizing</span>: border-box; <span class="comment">/* 重要！让width包含padding和border */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Flexbox布局实战"><a href="#Flexbox布局实战" class="headerlink" title="Flexbox布局实战"></a>Flexbox布局实战</h3><p>Flexbox是现代CSS布局的主力军，特别适合一维布局。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph TD    A["Flexbox容器"] --> B["主轴方向"]    A --> C["主轴对齐"]    A --> D["交叉轴对齐"]    A --> E["换行设置"]        B --> B1["flex-direction / row | column"]    C --> C1["justify-content / flex-start | center | flex-end | space-between | space-around"]    D --> D1["align-items / stretch | center | flex-start | flex-end"]    E --> E1["flex-wrap / nowrap | wrap"]</pre></div><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 导航栏布局示例 */</span></span><br><span class="line"><span class="selector-class">.navbar</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: flex;</span><br><span class="line">    <span class="attribute">justify-content</span>: space-between;</span><br><span class="line">    <span class="attribute">align-items</span>: center;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">1rem</span> <span class="number">2rem</span>;</span><br><span class="line">    <span class="attribute">background</span>: <span class="built_in">linear-gradient</span>(<span class="number">135deg</span>, <span class="number">#667eea</span> <span class="number">0%</span>, <span class="number">#764ba2</span> <span class="number">100%</span>);</span><br><span class="line">    <span class="attribute">border-radius</span>: <span class="number">12px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.logo</span> &#123;</span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">1.5rem</span>;</span><br><span class="line">    <span class="attribute">font-weight</span>: bold;</span><br><span class="line">    <span class="attribute">color</span>: white;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.nav-links</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: flex;</span><br><span class="line">    <span class="attribute">gap</span>: <span class="number">2rem</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.nav-links</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: white;</span><br><span class="line">    <span class="attribute">text-decoration</span>: none;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0.5rem</span> <span class="number">1rem</span>;</span><br><span class="line">    <span class="attribute">border-radius</span>: <span class="number">6px</span>;</span><br><span class="line">    <span class="attribute">transition</span>: background <span class="number">0.3s</span> ease;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.nav-links</span> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Grid布局实战"><a href="#Grid布局实战" class="headerlink" title="Grid布局实战"></a>Grid布局实战</h3><p>Grid是强大的二维布局系统，适合复杂页面布局。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 经典后台布局 */</span></span><br><span class="line"><span class="selector-class">.layout</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: grid;</span><br><span class="line">    <span class="attribute">grid-template-areas</span>: </span><br><span class="line">        <span class="string">&quot;header header header&quot;</span></span><br><span class="line">        <span class="string">&quot;sidebar main content&quot;</span></span><br><span class="line">        <span class="string">&quot;footer footer footer&quot;</span>;</span><br><span class="line">    <span class="attribute">grid-template-columns</span>: <span class="number">200px</span> <span class="number">1</span>fr <span class="number">200px</span>;</span><br><span class="line">    <span class="attribute">grid-template-rows</span>: auto <span class="number">1</span>fr auto;</span><br><span class="line">    <span class="attribute">gap</span>: <span class="number">1rem</span>;</span><br><span class="line">    <span class="attribute">min-height</span>: <span class="number">100vh</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.header</span> &#123; <span class="attribute">grid-area</span>: header; <span class="attribute">background</span>: <span class="number">#3498db</span>; &#125;</span><br><span class="line"><span class="selector-class">.sidebar</span> &#123; <span class="attribute">grid-area</span>: sidebar; <span class="attribute">background</span>: <span class="number">#2ecc71</span>; &#125;</span><br><span class="line"><span class="selector-class">.main</span> &#123; <span class="attribute">grid-area</span>: main; <span class="attribute">background</span>: <span class="number">#ecf0f1</span>; &#125;</span><br><span class="line"><span class="selector-class">.content</span> &#123; <span class="attribute">grid-area</span>: content; <span class="attribute">background</span>: <span class="number">#e74c3c</span>; &#125;</span><br><span class="line"><span class="selector-class">.footer</span> &#123; <span class="attribute">grid-area</span>: footer; <span class="attribute">background</span>: <span class="number">#95a5a6</span>; &#125;</span><br></pre></td></tr></table></figure><h3 id="响应式设计"><a href="#响应式设计" class="headerlink" title="响应式设计"></a>响应式设计</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 移动优先策略 */</span></span><br><span class="line"><span class="selector-class">.container</span> &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">15px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 平板设备 */</span></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">min-width</span>: <span class="number">768px</span>) &#123;</span><br><span class="line">    <span class="selector-class">.container</span> &#123;</span><br><span class="line">        <span class="attribute">width</span>: <span class="number">720px</span>;</span><br><span class="line">        <span class="attribute">margin</span>: <span class="number">0</span> auto;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="selector-class">.grid</span> &#123;</span><br><span class="line">        <span class="attribute">display</span>: grid;</span><br><span class="line">        <span class="attribute">grid-template-columns</span>: <span class="built_in">repeat</span>(<span class="number">2</span>, <span class="number">1</span>fr);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 桌面设备 */</span></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">min-width</span>: <span class="number">1024px</span>) &#123;</span><br><span class="line">    <span class="selector-class">.container</span> &#123;</span><br><span class="line">        <span class="attribute">width</span>: <span class="number">960px</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="selector-class">.grid</span> &#123;</span><br><span class="line">        <span class="attribute">grid-template-columns</span>: <span class="built_in">repeat</span>(<span class="number">3</span>, <span class="number">1</span>fr);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 大屏设备 */</span></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">min-width</span>: <span class="number">1280px</span>) &#123;</span><br><span class="line">    <span class="selector-class">.container</span> &#123;</span><br><span class="line">        <span class="attribute">width</span>: <span class="number">1140px</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="⚡-JavaScript：网页的灵魂交互"><a href="#⚡-JavaScript：网页的灵魂交互" class="headerlink" title="⚡ JavaScript：网页的灵魂交互"></a>⚡ JavaScript：网页的灵魂交互</h2><h3 id="JavaScript简介"><a href="#JavaScript简介" class="headerlink" title="JavaScript简介"></a>JavaScript简介</h3><p>JavaScript是一门<strong>动态、弱类型、基于原型的编程语言</strong>，最初用于浏览器端交互，如今已扩展到服务端（Node.js）、移动端（React Native）等多个领域。</p><h3 id="基础语法示例"><a href="#基础语法示例" class="headerlink" title="基础语法示例"></a>基础语法示例</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 变量声明</span></span><br><span class="line"><span class="keyword">let</span> name = <span class="string">&quot;前端学习者&quot;</span>;     <span class="comment">// 可变变量</span></span><br><span class="line"><span class="keyword">const</span> age = <span class="number">25</span>;             <span class="comment">// 常量（不可重新赋值）</span></span><br><span class="line"><span class="keyword">var</span> oldStyle = <span class="string">&quot;旧方式&quot;</span>;     <span class="comment">// 不推荐使用var</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据类型</span></span><br><span class="line"><span class="keyword">const</span> string = <span class="string">&quot;Hello World&quot;</span>;    <span class="comment">// 字符串</span></span><br><span class="line"><span class="keyword">const</span> number = <span class="number">42</span>;               <span class="comment">// 数字</span></span><br><span class="line"><span class="keyword">const</span> boolean = <span class="literal">true</span>;            <span class="comment">// 布尔值</span></span><br><span class="line"><span class="keyword">const</span> array = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];   <span class="comment">// 数组</span></span><br><span class="line"><span class="keyword">const</span> object = &#123;                <span class="comment">// 对象</span></span><br><span class="line">    <span class="attr">name</span>: <span class="string">&quot;学习者&quot;</span>,</span><br><span class="line">    <span class="attr">skills</span>: [<span class="string">&quot;HTML&quot;</span>, <span class="string">&quot;CSS&quot;</span>, <span class="string">&quot;JS&quot;</span>]</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数定义</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">greet</span>(<span class="params">name</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`你好，<span class="subst">$&#123;name&#125;</span>！欢迎学习Web前端 🚀`</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 箭头函数</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">add</span> = (<span class="params">a, b</span>) =&gt; a + b;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模板字符串</span></span><br><span class="line"><span class="keyword">const</span> message = <span class="string">`我是<span class="subst">$&#123;name&#125;</span>，今年<span class="subst">$&#123;age&#125;</span>岁，掌握<span class="subst">$&#123;object.skills.join(<span class="string">&quot;, &quot;</span>)&#125;</span>`</span>;</span><br></pre></td></tr></table></figure><h3 id="DOM操作实战"><a href="#DOM操作实战" class="headerlink" title="DOM操作实战"></a>DOM操作实战</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph TD    A["DOM操作流程"] --> B["获取元素"]    B --> C["操作元素"]    C --> D["修改内容"]    C --> E["修改样式"]    C --> F["添加事件"]    C --> G["创建元素"]    D --> H["textContent / innerHTML"]    E --> I["style / classList"]    F --> J["addEventListener"]    G --> K["createElement / appendChild"]</pre></div><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取页面元素</span></span><br><span class="line"><span class="keyword">const</span> title = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;h1&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> buttons = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">&#x27;.btn&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> container = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;container&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改元素内容</span></span><br><span class="line">title.<span class="property">textContent</span> = <span class="string">&#x27;新的标题 ✨&#x27;</span>;</span><br><span class="line">title.<span class="property">innerHTML</span> = <span class="string">&#x27;&lt;span&gt;HTML内容&lt;/span&gt;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改样式</span></span><br><span class="line">title.<span class="property">style</span>.<span class="property">color</span> = <span class="string">&#x27;#3498db&#x27;</span>;</span><br><span class="line">title.<span class="property">style</span>.<span class="property">fontSize</span> = <span class="string">&#x27;2rem&#x27;</span>;</span><br><span class="line">title.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">&#x27;highlight&#x27;</span>);</span><br><span class="line">title.<span class="property">classList</span>.<span class="title function_">remove</span>(<span class="string">&#x27;hidden&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 事件处理</span></span><br><span class="line">button.<span class="title function_">addEventListener</span>(<span class="string">&#x27;click&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;按钮被点击了！&#x27;</span>, event.<span class="property">target</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 阻止默认行为</span></span><br><span class="line">    event.<span class="title function_">preventDefault</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 事件委托示例</span></span><br><span class="line">    <span class="keyword">if</span> (event.<span class="property">target</span>.<span class="title function_">matches</span>(<span class="string">&#x27;.delete-btn&#x27;</span>)) &#123;</span><br><span class="line">        event.<span class="property">target</span>.<span class="property">parentElement</span>.<span class="title function_">remove</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建新元素</span></span><br><span class="line"><span class="keyword">const</span> newCard = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;div&#x27;</span>);</span><br><span class="line">newCard.<span class="property">className</span> = <span class="string">&#x27;card&#x27;</span>;</span><br><span class="line">newCard.<span class="property">innerHTML</span> = <span class="string">`</span></span><br><span class="line"><span class="string">    &lt;h3&gt;新卡片 🃏&lt;/h3&gt;</span></span><br><span class="line"><span class="string">    &lt;p&gt;这是动态创建的内容&lt;/p&gt;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;.cards-container&#x27;</span>).<span class="title function_">appendChild</span>(newCard);</span><br></pre></td></tr></table></figure><h3 id="异步编程：Promise与Async-Await"><a href="#异步编程：Promise与Async-Await" class="headerlink" title="异步编程：Promise与Async&#x2F;Await"></a>异步编程：Promise与Async&#x2F;Await</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Promise基本用法</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fetchUser</span>(<span class="params">userId</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (userId &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="title function_">resolve</span>(&#123; </span><br><span class="line">                    <span class="attr">id</span>: userId, </span><br><span class="line">                    <span class="attr">name</span>: <span class="string">`用户<span class="subst">$&#123;userId&#125;</span>`</span>,</span><br><span class="line">                    <span class="attr">email</span>: <span class="string">`user<span class="subst">$&#123;userId&#125;</span>@example.com`</span></span><br><span class="line">                &#125;);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;无效的用户ID&#x27;</span>));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用Promise</span></span><br><span class="line"><span class="title function_">fetchUser</span>(<span class="number">1</span>)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function"><span class="params">user</span> =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;获取到用户:&#x27;</span>, user))</span><br><span class="line">    .<span class="title function_">catch</span>(<span class="function"><span class="params">error</span> =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;错误:&#x27;</span>, error))</span><br><span class="line">    .<span class="title function_">finally</span>(<span class="function">() =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;请求完成 ✅&#x27;</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Async/Await语法（更现代）</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">getUserData</span>(<span class="params">userId</span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">`/api/users/<span class="subst">$&#123;userId&#125;</span>`</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (!response.<span class="property">ok</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`HTTP错误！状态码: <span class="subst">$&#123;response.status&#125;</span>`</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">const</span> user = <span class="keyword">await</span> response.<span class="title function_">json</span>();</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;用户数据:&#x27;</span>, user);</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;获取用户失败:&#x27;</span>, error);</span><br><span class="line">        <span class="keyword">throw</span> error;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 并行请求</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">fetchAllData</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> [users, posts, comments] = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>([</span><br><span class="line">        <span class="title function_">fetch</span>(<span class="string">&#x27;/api/users&#x27;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">r</span> =&gt;</span> r.<span class="title function_">json</span>()),</span><br><span class="line">        <span class="title function_">fetch</span>(<span class="string">&#x27;/api/posts&#x27;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">r</span> =&gt;</span> r.<span class="title function_">json</span>()),</span><br><span class="line">        <span class="title function_">fetch</span>(<span class="string">&#x27;/api/comments&#x27;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">r</span> =&gt;</span> r.<span class="title function_">json</span>())</span><br><span class="line">    ]);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> &#123; users, posts, comments &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="数组方法全家桶"><a href="#数组方法全家桶" class="headerlink" title="数组方法全家桶"></a>数组方法全家桶</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> products = [</span><br><span class="line">    &#123; <span class="attr">name</span>: <span class="string">&quot;笔记本电脑&quot;</span>, <span class="attr">price</span>: <span class="number">5999</span>, <span class="attr">category</span>: <span class="string">&quot;电子产品&quot;</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">name</span>: <span class="string">&quot;无线鼠标&quot;</span>, <span class="attr">price</span>: <span class="number">99</span>, <span class="attr">category</span>: <span class="string">&quot;电子产品&quot;</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">name</span>: <span class="string">&quot;人体工学椅&quot;</span>, <span class="attr">price</span>: <span class="number">899</span>, <span class="attr">category</span>: <span class="string">&quot;家具&quot;</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">name</span>: <span class="string">&quot;机械键盘&quot;</span>, <span class="attr">price</span>: <span class="number">399</span>, <span class="attr">category</span>: <span class="string">&quot;电子产品&quot;</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">name</span>: <span class="string">&quot;台灯&quot;</span>, <span class="attr">price</span>: <span class="number">129</span>, <span class="attr">category</span>: <span class="string">&quot;家具&quot;</span> &#125;</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// filter - 筛选</span></span><br><span class="line"><span class="keyword">const</span> electronics = products.<span class="title function_">filter</span>(<span class="function"><span class="params">p</span> =&gt;</span> p.<span class="property">category</span> === <span class="string">&quot;电子产品&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// map - 转换</span></span><br><span class="line"><span class="keyword">const</span> productNames = products.<span class="title function_">map</span>(<span class="function"><span class="params">p</span> =&gt;</span> p.<span class="property">name</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// find - 查找第一个匹配的</span></span><br><span class="line"><span class="keyword">const</span> expensiveItem = products.<span class="title function_">find</span>(<span class="function"><span class="params">p</span> =&gt;</span> p.<span class="property">price</span> &gt; <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// reduce - 汇总</span></span><br><span class="line"><span class="keyword">const</span> totalPrice = products.<span class="title function_">reduce</span>(<span class="function">(<span class="params">sum, p</span>) =&gt;</span> sum + p.<span class="property">price</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// sort - 排序</span></span><br><span class="line"><span class="keyword">const</span> sortedByPrice = [...products].<span class="title function_">sort</span>(<span class="function">(<span class="params">a, b</span>) =&gt;</span> a.<span class="property">price</span> - b.<span class="property">price</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链式调用</span></span><br><span class="line"><span class="keyword">const</span> expensiveElectronics = products</span><br><span class="line">    .<span class="title function_">filter</span>(<span class="function"><span class="params">p</span> =&gt;</span> p.<span class="property">category</span> === <span class="string">&quot;电子产品&quot;</span>)</span><br><span class="line">    .<span class="title function_">filter</span>(<span class="function"><span class="params">p</span> =&gt;</span> p.<span class="property">price</span> &gt; <span class="number">200</span>)</span><br><span class="line">    .<span class="title function_">sort</span>(<span class="function">(<span class="params">a, b</span>) =&gt;</span> b.<span class="property">price</span> - a.<span class="property">price</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;电子产品（价格&gt;200）:&#x27;</span>, expensiveElectronics);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;总价:&#x27;</span>, totalPrice);</span><br></pre></td></tr></table></figure><hr><h2 id="🔍-浏览器工作原理"><a href="#🔍-浏览器工作原理" class="headerlink" title="🔍 浏览器工作原理"></a>🔍 浏览器工作原理</h2><h3 id="浏览器的渲染流程"><a href="#浏览器的渲染流程" class="headerlink" title="浏览器的渲染流程"></a>浏览器的渲染流程</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph TD    A["用户访问URL"] --> B["DNS解析"]    B --> C["建立TCP连接"]    C --> D["发送HTTP请求"]    D --> E["服务器返回HTML"]    E --> F["解析HTML / 构建DOM树"]    F --> G["解析CSS / 构建CSSOM树"]    F --> H["执行JavaScript"]    G --> I["DOM + CSSOM"]    H --> I    I --> J["Render Tree / 渲染树"]    J --> K["Layout / 布局计算"]    K --> L["Paint / 绘制"]    L --> M["Composite / 合成"]    M --> N["显示在屏幕上"]        style F fill:#ff9    style G fill:#9f9    style J fill:#9cf    style K fill:#f9f</pre></div><h3 id="关键渲染路径详解"><a href="#关键渲染路径详解" class="headerlink" title="关键渲染路径详解"></a>关键渲染路径详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    subgraph CRP["关键渲染路径"]        A["1. 构建DOM树"] --> B["2. 构建CSSOM树"]        B --> C["3. 生成渲染树"]        C --> D["4. 布局计算"]        D --> E["5. 绘制与合成"]    end        subgraph Optimize["优化策略"]        F["精简CSS"]        G["优化选择器"]        H["延迟加载非关键CSS"]        I["压缩资源"]        J["使用GPU加速"]    end        style A fill:#f90    style B fill:#f90    style C fill:#09f    style D fill:#090    style E fill:#909</pre></div><h3 id="重排与重绘"><a href="#重排与重绘" class="headerlink" title="重排与重绘"></a>重排与重绘</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 低效：每次循环都触发重排</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">    element.<span class="property">style</span>.<span class="property">left</span> = i + <span class="string">&#x27;px&#x27;</span>; <span class="comment">// 触发重排</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 高效：使用CSS类批量修改</span></span><br><span class="line">element.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">&#x27;animate-position&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者先隐藏，批量修改，再显示</span></span><br><span class="line">element.<span class="property">style</span>.<span class="property">display</span> = <span class="string">&#x27;none&#x27;</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">    element.<span class="property">style</span>.<span class="property">left</span> = i + <span class="string">&#x27;px&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line">element.<span class="property">style</span>.<span class="property">display</span> = <span class="string">&#x27;block&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 最佳实践：使用transform</span></span><br><span class="line"><span class="keyword">const</span> startTime = performance.<span class="title function_">now</span>();</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">animate</span>(<span class="params"></span>) &#123;</span><br><span class="line">    element.<span class="property">style</span>.<span class="property">transform</span> = <span class="string">`translateX(<span class="subst">$&#123;position&#125;</span>px)`</span>;</span><br><span class="line">    <span class="keyword">if</span> (position &lt; <span class="number">100</span>) &#123;</span><br><span class="line">        <span class="title function_">requestAnimationFrame</span>(animate); <span class="comment">// 使用requestAnimationFrame</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="🛠️-前端工程化入门"><a href="#🛠️-前端工程化入门" class="headerlink" title="🛠️ 前端工程化入门"></a>🛠️ 前端工程化入门</h2><h3 id="前端工程化的意义"><a href="#前端工程化的意义" class="headerlink" title="前端工程化的意义"></a>前端工程化的意义</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph TD    A["前端工程化"] --> B["开发效率"]    A --> C["代码质量"]    A --> D["团队协作"]    A --> E["性能优化"]        B --> B1["模块化"]    B --> B2["组件化"]    B --> B3["脚手架工具"]        C --> C1["代码规范 / ESLint"]    C --> C2["单元测试 / Jest"]    C --> C3["类型检查 / TypeScript"]        D --> D1["Git工作流"]    D --> D2["代码审查"]    D --> D3["版本管理"]        E --> E1["构建工具 / Vite/Webpack"]    E --> E2["资源优化 / 压缩/合并"]    E --> E3["CDN部署"]</pre></div><h3 id="包管理器：npm与yarn"><a href="#包管理器：npm与yarn" class="headerlink" title="包管理器：npm与yarn"></a>包管理器：npm与yarn</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 初始化项目</span></span><br><span class="line">npm init -y</span><br><span class="line"><span class="comment"># 或者带参数初始化</span></span><br><span class="line">npm init --scope=username --<span class="built_in">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装依赖</span></span><br><span class="line">npm install lodash              <span class="comment"># 安装到 dependencies</span></span><br><span class="line">npm install --save-dev eslint  <span class="comment"># 安装到 devDependencies</span></span><br><span class="line">npm install -g vite             <span class="comment"># 全局安装</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用yarn</span></span><br><span class="line">yarn add lodash</span><br><span class="line">yarn add --dev eslint</span><br><span class="line">yarn global add vite</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行脚本</span></span><br><span class="line">npm run dev        <span class="comment"># 开发环境</span></span><br><span class="line">npm run build      <span class="comment"># 生产构建</span></span><br><span class="line">npm run <span class="built_in">test</span>       <span class="comment"># 运行测试</span></span><br><span class="line">npm run lint       <span class="comment"># 代码检查</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看已安装的包</span></span><br><span class="line">npm list</span><br><span class="line">npm list --depth=0  <span class="comment"># 只显示顶层依赖</span></span><br></pre></td></tr></table></figure><h3 id="构建工具对比"><a href="#构建工具对比" class="headerlink" title="构建工具对比"></a>构建工具对比</h3><table><thead><tr><th align="left">特性</th><th align="left">Webpack</th><th align="left">Vite</th><th align="left">Rollup</th></tr></thead><tbody><tr><td align="left">构建速度 🏃</td><td align="left">较慢</td><td align="left">快</td><td align="left">中等</td></tr><tr><td align="left">开发体验 🧑‍💻</td><td align="left">一般</td><td align="left">极佳</td><td align="left">一般</td></tr><tr><td align="left">生态 📦</td><td align="left">丰富</td><td align="left">成长中</td><td align="left">主要用于库</td></tr><tr><td align="left">配置复杂度 📝</td><td align="left">复杂</td><td align="left">简单</td><td align="left">中等</td></tr><tr><td align="left">Tree-shaking 🌲</td><td align="left">支持</td><td align="left">支持</td><td align="left">优秀</td></tr><tr><td align="left">热更新 ⚡</td><td align="left">较慢</td><td align="left">极快</td><td align="left">不支持</td></tr></tbody></table><h3 id="ESLint配置示例"><a href="#ESLint配置示例" class="headerlink" title="ESLint配置示例"></a>ESLint配置示例</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// .eslintrc.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;env&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;browser&quot;</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&quot;es2021&quot;</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&quot;node&quot;</span>: <span class="literal">true</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;extends&quot;</span>: [</span><br><span class="line">    <span class="string">&quot;eslint:recommended&quot;</span>,</span><br><span class="line">    <span class="string">&quot;plugin:react/recommended&quot;</span>,</span><br><span class="line">    <span class="string">&quot;plugin:react-hooks/recommended&quot;</span></span><br><span class="line">  ],</span><br><span class="line">  <span class="string">&quot;parserOptions&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;ecmaFeatures&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;jsx&quot;</span>: <span class="literal">true</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;ecmaVersion&quot;</span>: <span class="number">12</span>,</span><br><span class="line">    <span class="string">&quot;sourceType&quot;</span>: <span class="string">&quot;module&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;rules&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;indent&quot;</span>: [<span class="string">&quot;error&quot;</span>, <span class="number">2</span>],</span><br><span class="line">    <span class="string">&quot;linebreak-style&quot;</span>: [<span class="string">&quot;error&quot;</span>, <span class="string">&quot;unix&quot;</span>],</span><br><span class="line">    <span class="string">&quot;quotes&quot;</span>: [<span class="string">&quot;error&quot;</span>, <span class="string">&quot;single&quot;</span>],</span><br><span class="line">    <span class="string">&quot;semi&quot;</span>: [<span class="string">&quot;error&quot;</span>, <span class="string">&quot;always&quot;</span>],</span><br><span class="line">    <span class="string">&quot;no-unused-vars&quot;</span>: <span class="string">&quot;warn&quot;</span>,</span><br><span class="line">    <span class="string">&quot;react/prop-types&quot;</span>: <span class="string">&quot;off&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="🎯-学习路径与资源推荐"><a href="#🎯-学习路径与资源推荐" class="headerlink" title="🎯 学习路径与资源推荐"></a>🎯 学习路径与资源推荐</h2><h3 id="前端学习路线图"><a href="#前端学习路线图" class="headerlink" title="前端学习路线图"></a>前端学习路线图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>gantt    title 前端学习路线    dateFormat  YYYY-MM-DD    section 第一阶段    HTML基础       :2026-05-24, 7d    CSS基础        :2026-06-01, 7d    CSS布局实战    :2026-06-08, 7d    section 第二阶段    JavaScript基础 :2026-06-15, 14d    DOM操作        :2026-07-01, 7d    异步编程       :2026-07-08, 7d    section 第三阶段    Node.js基础    :2026-07-15, 14d    前端框架入门   :2026-08-01, 21d    项目实战       :2026-09-01, 30d</pre></div><h3 id="推荐学习资源-📚"><a href="#推荐学习资源-📚" class="headerlink" title="推荐学习资源 📚"></a>推荐学习资源 📚</h3><p><strong>在线教程</strong></p><ul><li><a href="https://developer.mozilla.org/zh-CN/">MDN Web Docs</a> - 权威Web技术文档</li><li><a href="https://es6.ruanyifeng.com/">阮一峰ES6教程</a> - JavaScript进阶必备</li><li><a href="https://moderncss.dev/">现代CSS</a> - 现代CSS最佳实践</li></ul><p><strong>视频课程</strong></p><ul><li>免费：B站前基础系列教程</li><li>付费：饥人谷、慕课网体系课</li></ul><p><strong>实践平台</strong></p><ul><li><a href="https://codepen.io/">CodePen</a> - 前端代码实验</li><li><a href="https://leetcode.cn/">LeetCode</a> - 算法训练</li><li><a href="https://github.com/">GitHub</a> - 开源项目学习</li></ul><h3 id="学习建议-💡"><a href="#学习建议-💡" class="headerlink" title="学习建议 💡"></a>学习建议 💡</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root["学习建议"]    基础要扎实      不要急于学框架      深入理解核心概念      多写代码多练习    动手实践      每天写代码      参与开源项目      构建个人项目    善用工具      Chrome DevTools      VS Code编辑器      Git版本控制    保持好奇      关注技术博客      参加技术社区      尝试新技术</pre></div><hr><h2 id="📖-总结"><a href="#📖-总结" class="headerlink" title="📖 总结"></a>📖 总结</h2><p>本文我们系统学习了Web前端开发的三大基石：</p><ul><li><strong>HTML</strong> 负责结构，语义化让网页更易访问</li><li><strong>CSS</strong> 负责表现，Flexbox和Grid让布局更灵活</li><li><strong>JavaScript</strong> 负责交互，ES6+让代码更优雅</li></ul><p>还了解了浏览器的工作原理和前端工程化的基本概念。这些知识将为后续学习React、Vue等框架打下坚实基础。</p><blockquote><p>🌟 <strong>记住</strong>：框架会过时，但基础不会。作为一名优秀的前端工程师，永远要重视基础的学习和积累。</p></blockquote><hr><p><em>如果你觉得这篇文章对你有帮助，欢迎转发给需要的朋友！有问题可以在评论区留言交流。</em></p>]]>
    </content>
    <id>https://blog.codenav.top/web-frontend-basics/</id>
    <link href="https://blog.codenav.top/web-frontend-basics/"/>
    <published>2026-05-24T12:47:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Web前端基础完全指南-🖥️"><a href="#Web前端基础完全指南-🖥️" class="headerlink" title="Web前端基础完全指南 🖥️"></a>Web前端基础完全指南 🖥️</h1><blockquote>
<p>前端开发是]]>
    </summary>
    <title>Web前端基础完全指南</title>
    <updated>2026-05-24T13:31:05.444Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="后端" scheme="https://blog.codenav.top/categories/%E5%90%8E%E7%AB%AF/"/>
    <category term="Elasticsearch" scheme="https://blog.codenav.top/tags/Elasticsearch/"/>
    <category term="ES" scheme="https://blog.codenav.top/tags/ES/"/>
    <category term="搜索引擎" scheme="https://blog.codenav.top/tags/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/"/>
    <category term="全文检索" scheme="https://blog.codenav.top/tags/%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2/"/>
    <category term="ELK" scheme="https://blog.codenav.top/tags/ELK/"/>
    <category term="大数据" scheme="https://blog.codenav.top/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <content>
      <![CDATA[<h1 id="Elasticsearch-全文搜索引擎详解：从入门到实战-🔍"><a href="#Elasticsearch-全文搜索引擎详解：从入门到实战-🔍" class="headerlink" title="Elasticsearch 全文搜索引擎详解：从入门到实战 🔍"></a>Elasticsearch 全文搜索引擎详解：从入门到实战 🔍</h1><blockquote><p>Elasticsearch（简称 ES）是一个基于 Lucene 构建的开源分布式全文搜索引擎，提供了强大的全文检索、数据分析和实时处理能力。本文将带你全面了解 Elasticsearch 的核心概念、索引管理、搜索查询以及与 Spring Boot 的集成实战！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80elasticsearch-%E6%A6%82%E8%BF%B0">一、Elasticsearch 概述</a></li><li><a href="#%E4%BA%8C%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E4%B8%8E%E6%9E%B6%E6%9E%84">二、核心概念与架构</a></li><li><a href="#%E4%B8%89%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8">三、快速入门</a></li><li><a href="#%E5%9B%9B%E7%B4%A2%E5%BC%95%E4%B8%8E%E6%96%87%E6%A1%A3%E6%93%8D%E4%BD%9C">四、索引与文档操作</a></li><li><a href="#%E4%BA%94%E6%90%9C%E7%B4%A2%E6%9F%A5%E8%AF%A2%E8%AF%A6%E8%A7%A3">五、搜索查询详解</a></li><li><a href="#%E5%85%AD%E8%81%9A%E5%90%88%E5%88%86%E6%9E%90">六、聚合分析</a></li><li><a href="#%E4%B8%83%E5%88%86%E5%B8%83%E5%BC%8F%E7%89%B9%E6%80%A7">七、分布式特性</a></li><li><a href="#%E5%85%ABspring-boot-%E9%9B%86%E6%88%90-es">八、Spring Boot 集成 ES</a></li><li><a href="#%E4%B9%9Delk-%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98">九、ELK 日志分析实战</a></li><li><a href="#%E5%8D%81%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%B8%80%E6%80%BB%E7%BB%93">十一、总结</a></li></ul><hr><h2 id="一、Elasticsearch-概述"><a href="#一、Elasticsearch-概述" class="headerlink" title="一、Elasticsearch 概述"></a>一、Elasticsearch 概述</h2><h3 id="1-1-什么是-Elasticsearch？"><a href="#1-1-什么是-Elasticsearch？" class="headerlink" title="1.1 什么是 Elasticsearch？"></a>1.1 什么是 Elasticsearch？</h3><p>Elasticsearch 是一个基于 <strong>Apache Lucene</strong> 构建的<strong>开源分布式全文搜索引擎</strong>，它主要特点包括：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔍 Elasticsearch 核心特点"] --> B["⚡ 全文检索"]    A --> C["📊 实时分析"]    A --> D["🌐 分布式架构"]    A --> E["📈 高可扩展"]        B --> B1["倒排索引\n分词器支持\n相关性评分"]        C --> C1["聚合分析\nBucket/Aggs\n数据可视化"]        D --> D1["数据分片\n副本机制\n自动负载均衡"]        E --> E1["水平扩展\n高可用\n故障转移"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0</pre></div><p><strong>为什么选择 Elasticsearch？</strong></p><ul><li>🌐 <strong>全球最流行的搜索引擎</strong>：在 DB-Engines 排名中稳居前三</li><li>📦 <strong>开箱即用</strong>：安装简单，配置友好</li><li>🔍 <strong>强大的搜索能力</strong>：支持全文检索、模糊匹配、短语搜索等</li><li>📊 <strong>实时分析</strong>：秒级聚合分析和数据可视化</li><li>🌐 <strong>分布式原生支持</strong>：天然支持海量数据水平扩展</li><li>🔌 <strong>丰富的客户端</strong>：支持 Java、Python、Go 等多种语言</li></ul><h3 id="1-2-Elasticsearch-能做什么？"><a href="#1-2-Elasticsearch-能做什么？" class="headerlink" title="1.2 Elasticsearch 能做什么？"></a>1.2 Elasticsearch 能做什么？</h3><table><thead><tr><th>应用场景</th><th>说明</th><th>典型案例</th></tr></thead><tbody><tr><td><strong>全文搜索</strong></td><td>关键词搜索、模糊匹配、同义词搜索</td><td>电商商品搜索、站内搜索、文档检索</td></tr><tr><td><strong>日志分析</strong></td><td>集中收集、搜索和分析日志</td><td>ELK 日志系统、Splunk 替代方案</td></tr><tr><td><strong>应用性能监控</strong></td><td>监控应用指标、追踪请求链路</td><td>APM 系统、性能分析</td></tr><tr><td><strong>安全分析</strong></td><td>安全日志分析、威胁检测</td><td>SIEM 系统、入侵检测</td></tr><tr><td><strong>业务分析</strong></td><td>数据聚合分析、BI 报表</td><td>用户行为分析、运营统计</td></tr></tbody></table><h3 id="1-3-Elasticsearch-vs-关系型数据库"><a href="#1-3-Elasticsearch-vs-关系型数据库" class="headerlink" title="1.3 Elasticsearch vs 关系型数据库"></a>1.3 Elasticsearch vs 关系型数据库</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🏗️ 数据模型对比"] --> B["关系型数据库"]    A --> C["Elasticsearch"]        B --> B1["Database 数据库"]    B1 --> B2["Table 表"]    B2 --> B3["Row 行"]    B2 --> B4["Column 列"]    B2 --> B5["Index 索引"]        C --> C1["Index 索引\n（类似数据库）"]    C1 --> C2["Type 类型\n（ES 7.x 已废弃）"]    C2 --> C3["Document 文档\n（类似行）"]    C2 --> C4["Field 字段\n（类似列）"]    C2 --> C5["Mapping 映射\n（类似表结构）"]        style B fill:#e3f2fd    style C fill:#c8e6c9</pre></div><p><strong>概念对应关系：</strong></p><table><thead><tr><th>关系型数据库</th><th>Elasticsearch</th><th>说明</th></tr></thead><tbody><tr><td>Database</td><td>Index</td><td>索引，数据的逻辑容器</td></tr><tr><td>Table</td><td>Type（已废弃）</td><td>类型，ES 7.x 后一个 Index 就是一个 Type</td></tr><tr><td>Row</td><td>Document</td><td>文档，JSON 格式的基本数据单元</td></tr><tr><td>Column</td><td>Field</td><td>字段，文档中的键值对</td></tr><tr><td>Schema</td><td>Mapping</td><td>映射，定义字段类型和索引方式</td></tr><tr><td>SQL</td><td>Query DSL</td><td>查询 DSL，ES 的查询语言</td></tr></tbody></table><hr><h2 id="二、核心概念与架构"><a href="#二、核心概念与架构" class="headerlink" title="二、核心概念与架构"></a>二、核心概念与架构</h2><h3 id="2-1-核心概念详解"><a href="#2-1-核心概念详解" class="headerlink" title="2.1 核心概念详解"></a>2.1 核心概念详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📋 ES 核心概念层级"] --> B["🔢 Cluster\n集群"]    A --> C["🖥️ Node\n节点"]    A --> D["📦 Index\n索引"]    A --> E["📝 Shard\n分片"]        B --> C    C --> D    D --> E        B1["一个或多个节点\n组成集群"]    C1["单个 ES 实例\n运行在 JVM 上"]    D1["文档的逻辑容器\n包含多个分片"]    E1["分片分为主分片\n和副本分片"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0    style E fill:#fff3e0</pre></div><h3 id="2-2-Elasticsearch-集群架构"><a href="#2-2-Elasticsearch-集群架构" class="headerlink" title="2.2 Elasticsearch 集群架构"></a>2.2 Elasticsearch 集群架构</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🌐 Elasticsearch 集群架构"] --> B["Cluster\n（集群）"]    B --> C["Node 1\n(Master Eligible)"]    B --> D["Node 2\n(Data Node)"]    B --> E["Node 3\n(Data Node)"]        C --> C1["Primary Shard 0"]    C --> C2["Replica Shard 1"]        D --> D1["Primary Shard 1"]    D --> D2["Replica Shard 2"]        E --> E1["Primary Shard 2"]    E --> E2["Replica Shard 0"]        style A fill:#fff3e0    style B fill:#c8e6c9</pre></div><p><strong>节点类型详解：</strong></p><table><thead><tr><th>节点类型</th><th>说明</th><th>配置</th></tr></thead><tbody><tr><td><strong>Master Node</strong></td><td>负责集群管理、索引创建删除、分片分配</td><td>node.master: true</td></tr><tr><td><strong>Data Node</strong></td><td>存储数据、执行查询</td><td>node.data: true</td></tr><tr><td><strong>Coordinating Node</strong></td><td>接收请求、分发到数据节点</td><td>node.master: false, node.data: false</td></tr><tr><td><strong>Ingest Node</strong></td><td>数据预处理、转换</td><td>node.ingest: true</td></tr></tbody></table><h3 id="2-3-倒排索引原理"><a href="#2-3-倒排索引原理" class="headerlink" title="2.3 倒排索引原理"></a>2.3 倒排索引原理</h3><p>倒排索引是 ES 实现快速全文搜索的核心技术，理解它对于更好地使用 ES 至关重要。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🔄 倒排索引原理"] --> B["📄 正向索引"]    A --> C["🔃 倒排索引"]        B --> B1["文档1：Elasticsearch 是搜索引擎"]    B1 --> B2["文档2：Elasticsearch 基于 Lucene"]    B1 --> B3["文档3：Lucene 是全文检索库"]        C --> C1["Elasticsearch → 文档1, 文档2"]    C1 --> C2["是 → 文档1"]    C2 --> C3["搜索 → 文档1, 文档3"]    C3 --> C4["Lucene → 文档2, 文档3"]        style B fill:#ffcdd2    style C fill:#c8e6c9</pre></div><p><strong>正向索引工作方式：</strong></p><ul><li>文档1 → 关键词1、关键词2、关键词3</li><li>搜索关键词1 时，需要遍历所有文档</li></ul><p><strong>倒排索引工作方式：</strong></p><ul><li>关键词1 → 文档1、文档3</li><li>搜索关键词1 时，直接定位到包含该词的文档</li></ul><h3 id="2-4-分词器（Analyzer）"><a href="#2-4-分词器（Analyzer）" class="headerlink" title="2.4 分词器（Analyzer）"></a>2.4 分词器（Analyzer）</h3><p>ES 的分词器决定了文本如何被切分成词条：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔤 分词器工作流程"] --> B["Character Filters\n字符过滤器"]    B --> C["Tokenizer\n分词器"]    C --> D["Token Filters\n词条过滤器"]    D --> E["输出词条"]        B --> B1["去除 HTML 标签\n转换特殊字符"]        C --> C1["standard\nik_smart\nik_max_word"]        D --> D1["小写化\n同义词替换\n停用词移除"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0</pre></div><p><strong>常见分词器：</strong></p><table><thead><tr><th>分词器</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>standard</strong></td><td>ES 默认分词器，按单词边界切分</td><td>英文</td></tr><tr><td><strong>ik_smart</strong></td><td>粗粒度分词，快速分词</td><td>中文搜索</td></tr><tr><td><strong>ik_max_word</strong></td><td>细粒度分词，最大程度切分</td><td>中文搜索（全面覆盖）</td></tr><tr><td><strong>pinyin</strong></td><td>中文拼音分词</td><td>拼音搜索</td></tr></tbody></table><hr><h2 id="三、快速入门"><a href="#三、快速入门" class="headerlink" title="三、快速入门"></a>三、快速入门</h2><h3 id="3-1-安装-Elasticsearch"><a href="#3-1-安装-Elasticsearch" class="headerlink" title="3.1 安装 Elasticsearch"></a>3.1 安装 Elasticsearch</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 下载 Elasticsearch 8.x</span></span><br><span class="line">wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.11.0-linux-x86_64.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 解压</span></span><br><span class="line">tar -xzf elasticsearch-8.11.0-linux-x86_64.tar.gz</span><br><span class="line"><span class="built_in">cd</span> elasticsearch-8.11.0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 配置 JVM 内存（可选）</span></span><br><span class="line">vim config/jvm.options.d/heap.options</span><br><span class="line">-Xms4g</span><br><span class="line">-Xmx4g</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 创建非 root 用户（ES 不允许 root 启动）</span></span><br><span class="line">useradd elasticsearch</span><br><span class="line"><span class="built_in">chown</span> -R elasticsearch:elasticsearch elasticsearch-8.11.0</span><br><span class="line">su elasticsearch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 启动</span></span><br><span class="line">./bin/elasticsearch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 后台启动</span></span><br><span class="line">./bin/elasticsearch -d -p pid</span><br><span class="line"></span><br><span class="line"><span class="comment"># 7. 验证启动</span></span><br><span class="line">curl http://localhost:9200</span><br></pre></td></tr></table></figure><p><strong>启动响应示例：</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;node-1&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;cluster_name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;elasticsearch&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;cluster_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;abc123...&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;number&quot;</span><span class="punctuation">:</span> <span class="string">&quot;8.11.0&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot; tagline&quot;</span><span class="punctuation">:</span> <span class="string">&quot;You Know, for Search&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="3-2-安装-Kibana（可视化工具）"><a href="#3-2-安装-Kibana（可视化工具）" class="headerlink" title="3.2 安装 Kibana（可视化工具）"></a>3.2 安装 Kibana（可视化工具）</h3><p>Kibana 是 ES 官方提供的可视化平台，用于查询、分析和可视化 ES 数据：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 下载 Kibana</span></span><br><span class="line">wget https://artifacts.elastic.co/downloads/kibana/kibana-8.11.0-linux-x86_64.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 解压</span></span><br><span class="line">tar -xzf kibana-8.11.0-linux-x86_64.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 配置（连接到 ES）</span></span><br><span class="line">vim config/kibana.yml</span><br><span class="line">server.host: <span class="string">&quot;0.0.0.0&quot;</span></span><br><span class="line">elasticsearch.hosts: [<span class="string">&quot;http://localhost:9200&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 启动</span></span><br><span class="line">./bin/kibana</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 访问 http://localhost:5601</span></span><br></pre></td></tr></table></figure><h3 id="3-3-集群健康检查"><a href="#3-3-集群健康检查" class="headerlink" title="3.3 集群健康检查"></a>3.3 集群健康检查</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查集群健康状态</span></span><br><span class="line">GET _cluster/health</span><br><span class="line"></span><br><span class="line"><span class="comment"># 返回：</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;cluster_name&quot;</span>: <span class="string">&quot;my-cluster&quot;</span>,</span><br><span class="line">  <span class="string">&quot;status&quot;</span>: <span class="string">&quot;green&quot;</span>,  <span class="comment"># green(完美)/yellow(副本有问题)/red(主分片有问题)</span></span><br><span class="line">  <span class="string">&quot;number_of_nodes&quot;</span>: 3,</span><br><span class="line">  <span class="string">&quot;active_shards&quot;</span>: 21,</span><br><span class="line">  <span class="string">&quot;relocating_shards&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;initializing_shards&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;unassigned_shards&quot;</span>: 0</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看节点列表</span></span><br><span class="line">GET _cat/nodes?v</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看分片状态</span></span><br><span class="line">GET _cat/shards?v</span><br></pre></td></tr></table></figure><h3 id="3-4-安全配置（ES-8-x-新增）"><a href="#3-4-安全配置（ES-8-x-新增）" class="headerlink" title="3.4 安全配置（ES 8.x 新增）"></a>3.4 安全配置（ES 8.x 新增）</h3><p>ES 8.x 默认开启了安全特性，需要配置认证：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成密码（首次安装时）</span></span><br><span class="line">./bin/elasticsearch-setup-passwords interactive</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用用户名密码访问</span></span><br><span class="line">curl -u elastic:password http://localhost:9200</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或关闭安全认证（开发环境）</span></span><br><span class="line">vim config/elasticsearch.yml</span><br><span class="line">xpack.security.enabled: <span class="literal">false</span></span><br></pre></td></tr></table></figure><hr><h2 id="四、索引与文档操作"><a href="#四、索引与文档操作" class="headerlink" title="四、索引与文档操作"></a>四、索引与文档操作</h2><h3 id="4-1-创建索引"><a href="#4-1-创建索引" class="headerlink" title="4.1 创建索引"></a>4.1 创建索引</h3><p>索引是 ES 中存储文档的逻辑容器，类似于关系型数据库中的表：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建索引（设置分片数和副本数）</span></span><br><span class="line">PUT /my_index</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;settings&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;number_of_shards&quot;</span>: 3,        <span class="comment"># 主分片数量</span></span><br><span class="line">    <span class="string">&quot;number_of_replicas&quot;</span>: 1,     <span class="comment"># 每个主分片的副本数</span></span><br><span class="line">    <span class="string">&quot;refresh_interval&quot;</span>: <span class="string">&quot;1s&quot;</span>,   <span class="comment"># 刷新间隔</span></span><br><span class="line">    <span class="string">&quot;analysis&quot;</span>: &#123;               <span class="comment"># 分析器配置</span></span><br><span class="line">      <span class="string">&quot;analyzer&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;my_analyzer&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;type&quot;</span>: <span class="string">&quot;custom&quot;</span>,</span><br><span class="line">          <span class="string">&quot;tokenizer&quot;</span>: <span class="string">&quot;ik_max_word&quot;</span>,</span><br><span class="line">          <span class="string">&quot;filter&quot;</span>: [<span class="string">&quot;lowercase&quot;</span>]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;title&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">        <span class="string">&quot;analyzer&quot;</span>: <span class="string">&quot;ik_max_word&quot;</span>,</span><br><span class="line">        <span class="string">&quot;fields&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;keyword&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span>  <span class="comment"># 支持精确查询</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;content&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">        <span class="string">&quot;analyzer&quot;</span>: <span class="string">&quot;ik_max_word&quot;</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;author&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span>    <span class="comment"># 关键字类型，不分词，精确匹配</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;tags&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span>    <span class="comment"># 标签，可多值</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;created_at&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;date&quot;</span>,     <span class="comment"># 日期类型</span></span><br><span class="line">        <span class="string">&quot;format&quot;</span>: <span class="string">&quot;yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis&quot;</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;views&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;long&quot;</span>       <span class="comment"># 长整型</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;price&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;double&quot;</span>     <span class="comment"># 双精度浮点型</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;is_published&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;boolean&quot;</span>    <span class="comment"># 布尔类型</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;location&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;geo_point&quot;</span>   <span class="comment"># 地理位置</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-2-查看索引信息"><a href="#4-2-查看索引信息" class="headerlink" title="4.2 查看索引信息"></a>4.2 查看索引信息</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看索引信息</span></span><br><span class="line">GET /my_index</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看所有索引</span></span><br><span class="line">GET _cat/indices?v</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看索引映射</span></span><br><span class="line">GET /my_index/_mapping</span><br></pre></td></tr></table></figure><h3 id="4-3-文档操作（CRUD）"><a href="#4-3-文档操作（CRUD）" class="headerlink" title="4.3 文档操作（CRUD）"></a>4.3 文档操作（CRUD）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ➕ 新增文档（自动生成 ID）</span></span><br><span class="line">POST /my_index/_doc</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;title&quot;</span>: <span class="string">&quot;Elasticsearch 入门教程&quot;</span>,</span><br><span class="line">  <span class="string">&quot;content&quot;</span>: <span class="string">&quot;本文介绍 Elasticsearch 的基本使用方法，包括索引、文档操作和搜索查询...&quot;</span>,</span><br><span class="line">  <span class="string">&quot;author&quot;</span>: <span class="string">&quot;张三&quot;</span>,</span><br><span class="line">  <span class="string">&quot;tags&quot;</span>: [<span class="string">&quot;ES&quot;</span>, <span class="string">&quot;搜索引擎&quot;</span>, <span class="string">&quot;入门&quot;</span>],</span><br><span class="line">  <span class="string">&quot;created_at&quot;</span>: <span class="string">&quot;2026-05-24&quot;</span>,</span><br><span class="line">  <span class="string">&quot;views&quot;</span>: 1000,</span><br><span class="line">  <span class="string">&quot;price&quot;</span>: 99.00,</span><br><span class="line">  <span class="string">&quot;is_published&quot;</span>: <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># ➕ 新增文档（指定 ID）</span></span><br><span class="line">PUT /my_index/_doc/1</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;title&quot;</span>: <span class="string">&quot;Elasticsearch 进阶指南&quot;</span>,</span><br><span class="line">  <span class="string">&quot;content&quot;</span>: <span class="string">&quot;深入学习 Elasticsearch 的高级特性...&quot;</span>,</span><br><span class="line">  <span class="string">&quot;author&quot;</span>: <span class="string">&quot;李四&quot;</span>,</span><br><span class="line">  <span class="string">&quot;tags&quot;</span>: [<span class="string">&quot;ES&quot;</span>, <span class="string">&quot;进阶&quot;</span>],</span><br><span class="line">  <span class="string">&quot;created_at&quot;</span>: <span class="string">&quot;2026-05-25&quot;</span>,</span><br><span class="line">  <span class="string">&quot;views&quot;</span>: 2000</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📖 查询文档</span></span><br><span class="line">GET /my_index/_doc/1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📖 查询文档（包含源数据）</span></span><br><span class="line">GET /my_index/_doc/1?_source=title,author</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📋 批量操作</span></span><br><span class="line">POST /_bulk</span><br><span class="line">&#123;<span class="string">&quot;index&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>,<span class="string">&quot;_id&quot;</span>:<span class="string">&quot;10&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;title&quot;</span>:<span class="string">&quot;批量文档1&quot;</span>,<span class="string">&quot;author&quot;</span>:<span class="string">&quot;王五&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;index&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>,<span class="string">&quot;_id&quot;</span>:<span class="string">&quot;11&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;title&quot;</span>:<span class="string">&quot;批量文档2&quot;</span>,<span class="string">&quot;author&quot;</span>:<span class="string">&quot;赵六&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># ✏️ 更新文档（全部更新）</span></span><br><span class="line">PUT /my_index/_doc/1</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;title&quot;</span>: <span class="string">&quot;Elasticsearch 进阶教程（更新版）&quot;</span>,</span><br><span class="line">  <span class="string">&quot;views&quot;</span>: 3000</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 🔄 部分更新</span></span><br><span class="line">POST /my_index/_update/1</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;doc&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;views&quot;</span>: 5000,</span><br><span class="line">    <span class="string">&quot;updated_at&quot;</span>: <span class="string">&quot;2026-05-26&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 🔄 使用脚本更新</span></span><br><span class="line">POST /my_index/_update/1</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;script&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;source&quot;</span>: <span class="string">&quot;ctx._source.views += params.count&quot;</span>,</span><br><span class="line">    <span class="string">&quot;params&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;count&quot;</span>: 100</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 🗑️ 删除文档</span></span><br><span class="line">DELETE /my_index/_doc/1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 🗑️ 删除索引</span></span><br><span class="line">DELETE /my_index</span><br></pre></td></tr></table></figure><h3 id="4-4-批量操作"><a href="#4-4-批量操作" class="headerlink" title="4.4 批量操作"></a>4.4 批量操作</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 批量新增</span></span><br><span class="line">POST /_bulk</span><br><span class="line">&#123;<span class="string">&quot;index&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;title&quot;</span>:<span class="string">&quot;文章1&quot;</span>,<span class="string">&quot;author&quot;</span>:<span class="string">&quot;作者A&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;index&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;title&quot;</span>:<span class="string">&quot;文章2&quot;</span>,<span class="string">&quot;author&quot;</span>:<span class="string">&quot;作者B&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;index&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;title&quot;</span>:<span class="string">&quot;文章3&quot;</span>,<span class="string">&quot;author&quot;</span>:<span class="string">&quot;作者A&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 混合操作</span></span><br><span class="line">POST /_bulk</span><br><span class="line">&#123;<span class="string">&quot;index&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;title&quot;</span>:<span class="string">&quot;新文章1&quot;</span>,<span class="string">&quot;author&quot;</span>:<span class="string">&quot;作者C&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;update&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>,<span class="string">&quot;_id&quot;</span>:<span class="string">&quot;1&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;doc&quot;</span>:&#123;<span class="string">&quot;views&quot;</span>:9999&#125;&#125;</span><br><span class="line">&#123;<span class="string">&quot;delete&quot;</span>:&#123;<span class="string">&quot;_index&quot;</span>:<span class="string">&quot;my_index&quot;</span>,<span class="string">&quot;_id&quot;</span>:<span class="string">&quot;2&quot;</span>&#125;&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、搜索查询详解"><a href="#五、搜索查询详解" class="headerlink" title="五、搜索查询详解"></a>五、搜索查询详解</h2><h3 id="5-1-全文搜索"><a href="#5-1-全文搜索" class="headerlink" title="5.1 全文搜索"></a>5.1 全文搜索</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 📝 单字段全文搜索</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;match&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;title&quot;</span>: <span class="string">&quot;Elasticsearch 入门&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;highlight&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;fields&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;title&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;pre_tags&quot;</span>: [<span class="string">&quot;&lt;em&gt;&quot;</span>],</span><br><span class="line">        <span class="string">&quot;post_tags&quot;</span>: [<span class="string">&quot;&lt;/em&gt;&quot;</span>]</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📝 多字段全文搜索</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;multi_match&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;query&quot;</span>: <span class="string">&quot;Elasticsearch 教程&quot;</span>,</span><br><span class="line">      <span class="string">&quot;fields&quot;</span>: [<span class="string">&quot;title^2&quot;</span>, <span class="string">&quot;content&quot;</span>],  <span class="comment"># ^2 表示 title 权重更高</span></span><br><span class="line">      <span class="string">&quot;type&quot;</span>: <span class="string">&quot;best_fields&quot;</span>  <span class="comment"># 最佳字段匹配</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-2-精确查询"><a href="#5-2-精确查询" class="headerlink" title="5.2 精确查询"></a>5.2 精确查询</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 🎯 精确匹配（term 查询不分词）</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;term&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;author&quot;</span>: <span class="string">&quot;张三&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 🎯 多值精确匹配</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;terms&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;tags&quot;</span>: [<span class="string">&quot;ES&quot;</span>, <span class="string">&quot;搜索引擎&quot;</span>]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-3-范围查询"><a href="#5-3-范围查询" class="headerlink" title="5.3 范围查询"></a>5.3 范围查询</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 📊 数值范围查询</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;range&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;views&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;gte&quot;</span>: 100,    <span class="comment"># greater than or equal</span></span><br><span class="line">        <span class="string">&quot;lte&quot;</span>: 1000,   <span class="comment"># less than or equal</span></span><br><span class="line">        <span class="string">&quot;boost&quot;</span>: 2.0   <span class="comment"># 权重提升</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📅 日期范围查询</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;range&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;created_at&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;gte&quot;</span>: <span class="string">&quot;2026-01-01&quot;</span>,</span><br><span class="line">        <span class="string">&quot;lte&quot;</span>: <span class="string">&quot;2026-05-31&quot;</span>,</span><br><span class="line">        <span class="string">&quot;format&quot;</span>: <span class="string">&quot;yyyy-MM-dd&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-4-布尔查询"><a href="#5-4-布尔查询" class="headerlink" title="5.4 布尔查询"></a>5.4 布尔查询</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 🔗 组合多个查询条件</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;bool&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;must&quot;</span>: [           <span class="comment"># 必须匹配（AND）</span></span><br><span class="line">        &#123; <span class="string">&quot;match&quot;</span>: &#123; <span class="string">&quot;title&quot;</span>: <span class="string">&quot;Elasticsearch&quot;</span> &#125; &#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="string">&quot;should&quot;</span>: [         <span class="comment"># 应该匹配（OR）</span></span><br><span class="line">        &#123; <span class="string">&quot;match&quot;</span>: &#123; <span class="string">&quot;content&quot;</span>: <span class="string">&quot;教程&quot;</span> &#125; &#125;,</span><br><span class="line">        &#123; <span class="string">&quot;match&quot;</span>: &#123; <span class="string">&quot;content&quot;</span>: <span class="string">&quot;入门&quot;</span> &#125; &#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="string">&quot;must_not&quot;</span>: [       <span class="comment"># 不能匹配（NOT）</span></span><br><span class="line">        &#123; <span class="string">&quot;term&quot;</span>: &#123; <span class="string">&quot;author&quot;</span>: <span class="string">&quot;测试用户&quot;</span> &#125; &#125;</span><br><span class="line">      ],</span><br><span class="line">      <span class="string">&quot;filter&quot;</span>: [         <span class="comment"># 过滤（不计分缓存）</span></span><br><span class="line">        &#123; <span class="string">&quot;range&quot;</span>: &#123; <span class="string">&quot;views&quot;</span>: &#123; <span class="string">&quot;gte&quot;</span>: 100 &#125; &#125; &#125;,</span><br><span class="line">        &#123; <span class="string">&quot;term&quot;</span>: &#123; <span class="string">&quot;is_published&quot;</span>: <span class="literal">true</span> &#125; &#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-5-高亮和排序"><a href="#5-5-高亮和排序" class="headerlink" title="5.5 高亮和排序"></a>5.5 高亮和排序</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 🎨 高亮显示匹配词</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;match&quot;</span>: &#123; <span class="string">&quot;title&quot;</span>: <span class="string">&quot;Elasticsearch&quot;</span> &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;highlight&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;fields&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;title&quot;</span>: &#123;&#125;,</span><br><span class="line">      <span class="string">&quot;content&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;fragment_size&quot;</span>: 150,    <span class="comment"># 摘要长度</span></span><br><span class="line">        <span class="string">&quot;number_of_fragments&quot;</span>: 3  <span class="comment"># 片段数量</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;pre_tags&quot;</span>: [<span class="string">&quot;&lt;mark&gt;&quot;</span>],</span><br><span class="line">    <span class="string">&quot;post_tags&quot;</span>: [<span class="string">&quot;&lt;/mark&gt;&quot;</span>]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📊 排序</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;query&quot;</span>: &#123; <span class="string">&quot;match_all&quot;</span>: &#123;&#125; &#125;,</span><br><span class="line">  <span class="string">&quot;sort&quot;</span>: [</span><br><span class="line">    &#123; <span class="string">&quot;views&quot;</span>: <span class="string">&quot;desc&quot;</span> &#125;,      <span class="comment"># 按浏览量降序</span></span><br><span class="line">    &#123; <span class="string">&quot;created_at&quot;</span>: <span class="string">&quot;asc&quot;</span> &#125;,   <span class="comment"># 按时间升序</span></span><br><span class="line">    <span class="string">&quot;_score&quot;</span>                    <span class="comment"># 按相关性得分</span></span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、聚合分析"><a href="#六、聚合分析" class="headerlink" title="六、聚合分析"></a>六、聚合分析</h2><h3 id="6-1-聚合查询概述"><a href="#6-1-聚合查询概述" class="headerlink" title="6.1 聚合查询概述"></a>6.1 聚合查询概述</h3><p>ES 的聚合功能非常强大，支持多种聚合分析：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📊 ES 聚合类型"] --> B["Bucket Aggregations\n桶聚合"]    A --> C["Metric Aggregations\n指标聚合"]    A --> D["Pipeline Aggregations\n管道聚合"]        B --> B1["terms 按字段值分桶\nrange 按范围分桶\ndate_histogram 按日期分桶"]        C --> C1["avg/sum/min/max\nstats 多统计\ncardinality 基数"]        D --> D1["parent_bucket\nsibling_bucket"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd</pre></div><h3 id="6-2-桶聚合示例"><a href="#6-2-桶聚合示例" class="headerlink" title="6.2 桶聚合示例"></a>6.2 桶聚合示例</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 📈 按作者分桶统计文章数量</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;size&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;by_author&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;terms&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;author&quot;</span>,</span><br><span class="line">        <span class="string">&quot;size&quot;</span>: 10,          <span class="comment"># 返回前 10 个桶</span></span><br><span class="line">        <span class="string">&quot;order&quot;</span>: &#123; <span class="string">&quot;_count&quot;</span>: <span class="string">&quot;desc&quot;</span> &#125;  <span class="comment"># 按文档数降序</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 📅 按月份分桶统计</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;size&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;by_month&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;date_histogram&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;created_at&quot;</span>,</span><br><span class="line">        <span class="string">&quot;calendar_interval&quot;</span>: <span class="string">&quot;month&quot;</span>,</span><br><span class="line">        <span class="string">&quot;format&quot;</span>: <span class="string">&quot;yyyy-MM&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 💰 按价格范围分桶</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;size&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;price_ranges&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;range&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;price&quot;</span>,</span><br><span class="line">        <span class="string">&quot;ranges&quot;</span>: [</span><br><span class="line">          &#123; <span class="string">&quot;key&quot;</span>: <span class="string">&quot;free&quot;</span>, <span class="string">&quot;to&quot;</span>: 0 &#125;,</span><br><span class="line">          &#123; <span class="string">&quot;key&quot;</span>: <span class="string">&quot;cheap&quot;</span>, <span class="string">&quot;from&quot;</span>: 0, <span class="string">&quot;to&quot;</span>: 50 &#125;,</span><br><span class="line">          &#123; <span class="string">&quot;key&quot;</span>: <span class="string">&quot;normal&quot;</span>, <span class="string">&quot;from&quot;</span>: 50, <span class="string">&quot;to&quot;</span>: 100 &#125;,</span><br><span class="line">          &#123; <span class="string">&quot;key&quot;</span>: <span class="string">&quot;expensive&quot;</span>, <span class="string">&quot;from&quot;</span>: 100 &#125;</span><br><span class="line">        ]</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-指标聚合示例"><a href="#6-3-指标聚合示例" class="headerlink" title="6.3 指标聚合示例"></a>6.3 指标聚合示例</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 📊 多指标统计</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;size&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;views_stats&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;stats&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;views&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;max_views&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;max&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;views&quot;</span> &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&quot;avg_price&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;avg&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;price&quot;</span> &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 🧮 去重统计</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;size&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;unique_authors&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;cardinality&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;author&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-4-嵌套聚合示例"><a href="#6-4-嵌套聚合示例" class="headerlink" title="6.4 嵌套聚合示例"></a>6.4 嵌套聚合示例</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 🔗 桶 + 指标嵌套聚合</span></span><br><span class="line">GET /my_index/_search</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;size&quot;</span>: 0,</span><br><span class="line">  <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;by_author&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;terms&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;author&quot;</span>,</span><br><span class="line">        <span class="string">&quot;size&quot;</span>: 10</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;aggs&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;avg_views&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;avg&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;views&quot;</span> &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;total_views&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;sum&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;views&quot;</span> &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、分布式特性"><a href="#七、分布式特性" class="headerlink" title="七、分布式特性"></a>七、分布式特性</h2><h3 id="7-1-分片与副本机制"><a href="#7-1-分片与副本机制" class="headerlink" title="7.1 分片与副本机制"></a>7.1 分片与副本机制</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📦 分片机制"] --> B["Primary Shard\n主分片"]    A --> C["Replica Shard\n副本分片"]        B --> B1["处理读写请求\n数量创建后不可变"]        C --> C1["主分片的数据副本\n提供高可用"]        style B fill:#c8e6c9    style C fill:#e3f2fd</pre></div><p><strong>分片数量的选择建议：</strong></p><table><thead><tr><th>数据量</th><th>推荐分片数</th><th>说明</th></tr></thead><tbody><tr><td>&lt; 10GB</td><td>1-2</td><td>小数据集</td></tr><tr><td>10-50GB</td><td>3-5</td><td>中等数据集</td></tr><tr><td>50-100GB</td><td>5-10</td><td>大数据集</td></tr><tr><td>&gt; 100GB</td><td>根据节点数决定</td><td>通常 20-30GB&#x2F;分片</td></tr></tbody></table><h3 id="7-2-数据写入流程"><a href="#7-2-数据写入流程" class="headerlink" title="7.2 数据写入流程"></a>7.2 数据写入流程</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["✍️ 数据写入流程"] --> B["客户端请求"]    B --> C["协调节点"]    C --> D["主分片"]    D --> E["同步到副本"]    E --> F["返回成功"]        style A fill:#fff3e0    style C fill:#c8e6c9</pre></div><h3 id="7-3-数据一致性级别"><a href="#7-3-数据一致性级别" class="headerlink" title="7.3 数据一致性级别"></a>7.3 数据一致性级别</h3><table><thead><tr><th>级别</th><th>说明</th><th>性能</th></tr></thead><tbody><tr><td><strong>consistency&#x3D;one</strong></td><td>主分片和至少一个副本确认</td><td>较快</td></tr><tr><td><strong>consistency&#x3D;quorum</strong></td><td>半数以上分片确认</td><td>中等</td></tr><tr><td><strong>consistency&#x3D;all</strong></td><td>所有副本确认</td><td>较慢</td></tr></tbody></table><hr><h2 id="八、Spring-Boot-集成-ES"><a href="#八、Spring-Boot-集成-ES" class="headerlink" title="八、Spring Boot 集成 ES"></a>八、Spring Boot 集成 ES</h2><h3 id="8-1-添加依赖"><a href="#8-1-添加依赖" class="headerlink" title="8.1 添加依赖"></a>8.1 添加依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Spring Data Elasticsearch --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-elasticsearch<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="8-2-配置"><a href="#8-2-配置" class="headerlink" title="8.2 配置"></a>8.2 配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">elasticsearch:</span></span><br><span class="line">    <span class="attr">uris:</span> <span class="string">http://localhost:9200</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">elastic</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">password</span></span><br><span class="line">    <span class="attr">connection-timeout:</span> <span class="string">5s</span></span><br><span class="line">    <span class="attr">socket-timeout:</span> <span class="string">30s</span></span><br></pre></td></tr></table></figure><h3 id="8-3-实体类定义"><a href="#8-3-实体类定义" class="headerlink" title="8.3 实体类定义"></a>8.3 实体类定义</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 博客文章实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Document(indexName = &quot;blog_index&quot;)</span>  <span class="comment">// 索引名称</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Blog</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span>                                  <span class="comment">// 文档 ID</span></span><br><span class="line">    <span class="keyword">private</span> String id;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Text, analyzer = &quot;ik_max_word&quot;)</span>  <span class="comment">// 文本类型，支持分词</span></span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Text, analyzer = &quot;ik_max_word&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String content;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Keyword)</span>     <span class="comment">// 关键字类型，不分词</span></span><br><span class="line">    <span class="keyword">private</span> String author;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Keyword)</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; tags;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Long)</span></span><br><span class="line">    <span class="keyword">private</span> Long views;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Double)</span></span><br><span class="line">    <span class="keyword">private</span> Double price;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Boolean)</span></span><br><span class="line">    <span class="keyword">private</span> Boolean isPublished;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createdAt;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime updatedAt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-4-Repository-接口"><a href="#8-4-Repository-接口" class="headerlink" title="8.4 Repository 接口"></a>8.4 Repository 接口</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Blog Repository 接口</span></span><br><span class="line"><span class="comment"> * 继承 ElasticsearchRepository，自动具备 CRUD 功能</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BlogRepository</span> <span class="keyword">extends</span> <span class="title class_">ElasticsearchRepository</span>&lt;Blog, String&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 🎯 根据标题查询</span></span><br><span class="line">    List&lt;Blog&gt; <span class="title function_">findByTitle</span><span class="params">(String title)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 🎯 根据作者查询</span></span><br><span class="line">    List&lt;Blog&gt; <span class="title function_">findByAuthor</span><span class="params">(String author)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 📊 按作者查询并排序</span></span><br><span class="line">    List&lt;Blog&gt; <span class="title function_">findByAuthorOrderByCreatedAtDesc</span><span class="params">(String author)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 📈 查询浏览量大于指定值的文章</span></span><br><span class="line">    List&lt;Blog&gt; <span class="title function_">findByViewsGreaterThan</span><span class="params">(Long views)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 🔍 模糊查询标题</span></span><br><span class="line">    List&lt;Blog&gt; <span class="title function_">findByTitleContaining</span><span class="params">(String keyword)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 🏷️ 按标签查询</span></span><br><span class="line">    List&lt;Blog&gt; <span class="title function_">findByTagsContaining</span><span class="params">(String tag)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-5-自定义查询方法"><a href="#8-5-自定义查询方法" class="headerlink" title="8.5 自定义查询方法"></a>8.5 自定义查询方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义复杂查询</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BlogService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ElasticsearchOperations elasticsearchOperations;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BlogRepository blogRepository;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 复杂条件查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> SearchHits&lt;Blog&gt; <span class="title function_">searchBlogs</span><span class="params">(String keyword, String author, Long minViews)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 构建查询条件</span></span><br><span class="line">        <span class="type">Criteria</span> <span class="variable">criteria</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Criteria</span>(<span class="string">&quot;title&quot;</span>).matches(keyword)</span><br><span class="line">            .and(<span class="keyword">new</span> <span class="title class_">Criteria</span>(<span class="string">&quot;author&quot;</span>).is(author));</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (minViews != <span class="literal">null</span>) &#123;</span><br><span class="line">            criteria = criteria.and(<span class="keyword">new</span> <span class="title class_">Criteria</span>(<span class="string">&quot;views&quot;</span>).greaterThanEqual(minViews));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Query</span> <span class="variable">query</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CriteriaQuery</span>(criteria);</span><br><span class="line">        <span class="keyword">return</span> elasticsearchOperations.search(query, Blog.class);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Page&lt;Blog&gt; <span class="title function_">searchByPage</span><span class="params">(String keyword, <span class="type">int</span> page, <span class="type">int</span> size)</span> &#123;</span><br><span class="line">        <span class="type">Query</span> <span class="variable">query</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringQuery</span>(<span class="string">&quot;&#123;\&quot;match\&quot;:&#123;\&quot;title\&quot;:\&quot;&quot;</span> + keyword + <span class="string">&quot;\&quot;&#125;&#125;&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">Pageable</span> <span class="variable">pageable</span> <span class="operator">=</span> PageRequest.of(page, size);</span><br><span class="line">        query.setPageable(pageable);</span><br><span class="line">        </span><br><span class="line">        SearchHits&lt;Blog&gt; hits = elasticsearchOperations.search(query, Blog.class);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageImpl</span>&lt;&gt;(hits.getSearchHits(), pageable, hits.getTotalHits());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 聚合查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String, Long&gt; <span class="title function_">getAuthorArticleCounts</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Criteria</span> <span class="variable">criteria</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Criteria</span>();</span><br><span class="line">        <span class="type">Query</span> <span class="variable">query</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CriteriaQuery</span>(criteria);</span><br><span class="line">        </span><br><span class="line">        SearchHits&lt;Blog&gt; hits = elasticsearchOperations.search(query, Blog.class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 简单聚合：按作者分组统计</span></span><br><span class="line">        Map&lt;String, Long&gt; result = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        hits.getSearchHits().forEach(hit -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">author</span> <span class="operator">=</span> hit.getContent().getAuthor();</span><br><span class="line">            result.merge(author, <span class="number">1L</span>, Long::sum);</span><br><span class="line">        &#125;);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、ELK-日志分析实战"><a href="#九、ELK-日志分析实战" class="headerlink" title="九、ELK 日志分析实战"></a>九、ELK 日志分析实战</h2><h3 id="9-1-ELK-架构概述"><a href="#9-1-ELK-架构概述" class="headerlink" title="9.1 ELK 架构概述"></a>9.1 ELK 架构概述</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📊 ELK Stack 架构"] --> B["Logstash/Beat\n日志收集"]    B --> C["Elasticsearch\n存储 + 检索"]    C --> D["Kibana\n可视化分析"]        B --> B1["Filebeat 日志文件收集"]    B1 --> B2["Logstash 数据处理"]    B2 --> B3["Kafka 消息队列（可选）"]    B3 --> C        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0</pre></div><h3 id="9-2-Filebeat-配置"><a href="#9-2-Filebeat-配置" class="headerlink" title="9.2 Filebeat 配置"></a>9.2 Filebeat 配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># filebeat.yml</span></span><br><span class="line"><span class="attr">filebeat.inputs:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">type:</span> <span class="string">log</span></span><br><span class="line">  <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">paths:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">/var/log/myapp/*.log</span></span><br><span class="line">  <span class="attr">fields:</span></span><br><span class="line">    <span class="attr">app:</span> <span class="string">myapp</span></span><br><span class="line">    <span class="attr">env:</span> <span class="string">production</span></span><br><span class="line"></span><br><span class="line"><span class="attr">output.elasticsearch:</span></span><br><span class="line">  <span class="attr">hosts:</span> [<span class="string">&quot;localhost:9200&quot;</span>]</span><br><span class="line">  <span class="attr">index:</span> <span class="string">&quot;app-logs-<span class="template-variable">%&#123;+yyyy.MM.dd&#125;</span>&quot;</span></span><br></pre></td></tr></table></figure><h3 id="9-3-Logstash-配置"><a href="#9-3-Logstash-配置" class="headerlink" title="9.3 Logstash 配置"></a>9.3 Logstash 配置</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"># pipeline.conf</span><br><span class="line">input &#123;</span><br><span class="line">  beats &#123;</span><br><span class="line">    port =&gt; 5044</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">filter &#123;</span><br><span class="line">  json &#123;</span><br><span class="line">    source =&gt; &quot;message&quot;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  date &#123;</span><br><span class="line">    match =&gt; [&quot;timestamp&quot;, &quot;ISO8601&quot;]</span><br><span class="line">    target =&gt; &quot;@timestamp&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts =&gt; [&quot;localhost:9200&quot;]</span><br><span class="line">    index =&gt; &quot;app-logs-%&#123;+YYYY.MM.dd&#125;&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十、常见问题与最佳实践"><a href="#十、常见问题与最佳实践" class="headerlink" title="十、常见问题与最佳实践"></a>十、常见问题与最佳实践</h2><h3 id="10-1-常见问题"><a href="#10-1-常见问题" class="headerlink" title="10.1 常见问题"></a>10.1 常见问题</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["🧹 分片分配不均"]    A --> C["⚠️ 内存不足"]    A --> D["🐌 查询慢"]        B --> B1["重启节点后\n分片重分配"]    B1 --> B2["使用 reroute 手动调整"]        C --> C1["JVM 内存设置过大\n系统内存不足"]    C1 --> C2["配置 bootstrap.memory_lock"]        D --> D1["分片数量不合理\n查询写法不优"]    D1 --> D2["增加副本\n优化查询"]        style A fill:#fff3e0</pre></div><h3 id="10-2-最佳实践"><a href="#10-2-最佳实践" class="headerlink" title="10.2 最佳实践"></a>10.2 最佳实践</h3><table><thead><tr><th>实践</th><th>说明</th><th>推荐程度</th></tr></thead><tbody><tr><td><strong>分片大小控制</strong></td><td>每个分片建议 20-50GB</td><td>✅✅✅</td></tr><tr><td><strong>副本数量</strong></td><td>生产环境至少 1 副本</td><td>✅✅✅</td></tr><tr><td><strong>冷热分离</strong></td><td>热数据 SSD，冷数据 HDD</td><td>✅✅</td></tr><tr><td><strong>使用别名</strong></td><td>通过别名切换索引</td><td>✅✅</td></tr><tr><td><strong>路由优化</strong></td><td>合理使用 routing 参数</td><td>✅✅</td></tr></tbody></table><hr><h2 id="十一、总结"><a href="#十一、总结" class="headerlink" title="十一、总结"></a>十一、总结</h2><h3 id="11-1-核心知识点回顾"><a href="#11-1-核心知识点回顾" class="headerlink" title="11.1 核心知识点回顾"></a>11.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Elasticsearch))    核心概念      Cluster 集群      Node 节点      Shard 分片      Document 文档      Index 索引    查询类型      Match 全文搜索      Term 精确查询      Range 范围查询      Bool 布尔查询    聚合分析      Bucket 桶聚合      Metric 指标聚合      Pipeline 管道聚合    Spring Boot      ElasticsearchRepository      ElasticsearchOperations      Document 注解    ELK 生态      Logstash 收集      Elasticsearch 存储      Kibana 可视化</pre></div><h3 id="11-2-学习路线"><a href="#11-2-学习路线" class="headerlink" title="11.2 学习路线"></a>11.2 学习路线</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["ES 学习路线"] --> B["第一阶段\n基础入门"]    B --> C["第二阶段\n查询进阶"]    C --> D["第三阶段\n聚合分析"]    D --> E["第四阶段\n集群运维"]        B --> B1["核心概念\n索引文档操作"]    C --> C1["DSL 查询\n高亮排序"]    D --> D1["桶聚合\n指标聚合"]    E --> E1["分片调优\n性能优化"]        style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Elasticsearch 是现代搜索和日志分析的核心组件，掌握其使用对后端开发者来说至关重要。希望本文能帮助你建立完整的 ES 知识体系，在项目中游刃有余地使用 Elasticsearch！🔍</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/elasticsearch-guide/</id>
    <link href="https://blog.codenav.top/elasticsearch-guide/"/>
    <published>2026-05-24T11:39:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Elasticsearch-全文搜索引擎详解：从入门到实战-🔍"><a href="#Elasticsearch-全文搜索引擎详解：从入门到实战-🔍" class="headerlink" title="Elasticsearch 全文搜索引擎详解：从入门到实]]>
    </summary>
    <title>Elasticsearch 全文搜索引擎详解：从入门到实战 🔍</title>
    <updated>2026-05-24T11:44:02.209Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="MQ" scheme="https://blog.codenav.top/tags/MQ/"/>
    <category term="消息队列" scheme="https://blog.codenav.top/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
    <category term="RabbitMQ" scheme="https://blog.codenav.top/tags/RabbitMQ/"/>
    <category term="Kafka" scheme="https://blog.codenav.top/tags/Kafka/"/>
    <category term="RocketMQ" scheme="https://blog.codenav.top/tags/RocketMQ/"/>
    <category term="中间件" scheme="https://blog.codenav.top/tags/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
    <content>
      <![CDATA[<h1 id="消息队列（MQ）详解：从入门到实战-📮"><a href="#消息队列（MQ）详解：从入门到实战-📮" class="headerlink" title="消息队列（MQ）详解：从入门到实战 📮"></a>消息队列（MQ）详解：从入门到实战 📮</h1><blockquote><p>消息队列（Message Queue，简称 MQ）是分布式系统中实现异步通信的核心组件，它允许应用之间通过消息传递来进行数据交换，实现系统间的解耦、削峰填谷和最终一致性。本文将带你全面理解消息队列的核心概念、主流产品对比以及实战应用！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97">一、为什么需要消息队列？</a></li><li><a href="#%E4%BA%8C%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5">二、消息队列核心概念</a></li><li><a href="#%E4%B8%89%E4%B8%BB%E6%B5%81%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%AF%B9%E6%AF%94">三、主流消息队列对比</a></li><li><a href="#%E5%9B%9Brabbitmq-%E8%AF%A6%E8%A7%A3">四、RabbitMQ 详解</a></li><li><a href="#%E4%BA%94kafka-%E8%AF%A6%E8%A7%A3">五、Kafka 详解</a></li><li><a href="#%E5%85%ADrocketmq-%E8%AF%A6%E8%A7%A3">六、RocketMQ 详解</a></li><li><a href="#%E4%B8%83%E6%B6%88%E6%81%AF%E5%8F%AF%E9%9D%A0%E6%80%A7%E5%92%8C%E4%BA%8B%E5%8A%A1">七、消息可靠性和事务</a></li><li><a href="#%E5%85%ABspring-boot-%E9%9B%86%E6%88%90-mq">八、Spring Boot 集成 MQ</a></li><li><a href="#%E4%B9%9D%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">九、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E6%80%BB%E7%BB%93">十、总结</a></li></ul><hr><h2 id="一、为什么需要消息队列？"><a href="#一、为什么需要消息队列？" class="headerlink" title="一、为什么需要消息队列？"></a>一、为什么需要消息队列？</h2><h3 id="1-1-传统同步调用的问题"><a href="#1-1-传统同步调用的问题" class="headerlink" title="1.1 传统同步调用的问题"></a>1.1 传统同步调用的问题</h3><p>在传统的系统架构中，服务之间的调用通常是<strong>同步的</strong>。这意味着调用方需要等待被调用方返回结果才能继续执行。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["❌ 同步调用问题"] --> B["🔗 系统紧耦合"]    B --> B1["一个系统故障\n影响所有调用方"]        A --> C["⏰ 响应延迟"]    C --> C1["调用链长\n响应时间长"]        A --> D["📈 无法应对突发流量"]    D --> D1["秒杀场景\n系统崩溃"]        A --> E["🧩 扩展困难"]    E --> E1["系统改造\n影响上下游"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#ffcdd2    style D fill:#ffcdd2    style E fill:#ffcdd2</pre></div><h3 id="1-2-消息队列如何解决问题"><a href="#1-2-消息队列如何解决问题" class="headerlink" title="1.2 消息队列如何解决问题"></a>1.2 消息队列如何解决问题</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["✅ 消息队列解决方案"] --> B["🔓 系统解耦"]    A --> C["⏱️ 异步处理"]    A --> D["📊 削峰填谷"]    A --> E["🔄 可扩展性"]        B --> B1["生产者和消费者\n无需互相等待"]        C --> C1["非阻塞通信\n提升吞吐量"]        D --> D1["高峰期消息积压\n低谷期慢慢消费"]        E --> E1["轻松增加\n消费者实例"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#c8e6c9    style E fill:#c8e6c9</pre></div><h3 id="1-3-消息队列的典型应用场景"><a href="#1-3-消息队列的典型应用场景" class="headerlink" title="1.3 消息队列的典型应用场景"></a>1.3 消息队列的典型应用场景</h3><table><thead><tr><th>场景</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>异步处理</strong></td><td>将耗时操作异步化，提升系统响应</td><td>用户注册后发送邮件、短信</td></tr><tr><td><strong>系统解耦</strong></td><td>降低系统间依赖，提高灵活性</td><td>订单完成后通知库存、物流</td></tr><tr><td><strong>流量削峰</strong></td><td>缓解突发流量，保护系统</td><td>秒杀、抢购场景</td></tr><tr><td><strong>日志处理</strong></td><td>异步收集和汇总日志</td><td>ELK 日志系统</td></tr><tr><td><strong>消息通知</strong></td><td>实时推送通知</td><td>订单状态变更通知</td></tr></tbody></table><h3 id="1-4-同步-vs-异步对比"><a href="#1-4-同步-vs-异步对比" class="headerlink" title="1.4 同步 vs 异步对比"></a>1.4 同步 vs 异步对比</h3><table><thead><tr><th>对比维度</th><th>同步调用</th><th>异步消息队列</th></tr></thead><tbody><tr><td><strong>响应时间</strong></td><td>调用方需等待</td><td>立即返回</td></tr><tr><td><strong>耦合度</strong></td><td>高</td><td>低</td></tr><tr><td><strong>可用性</strong></td><td>依赖方全存活</td><td>消息持久化保证</td></tr><tr><td><strong>扩展性</strong></td><td>困难</td><td>容易</td></tr><tr><td><strong>适用场景</strong></td><td>强一致性需求</td><td>最终一致性需求</td></tr></tbody></table><hr><h2 id="二、消息队列核心概念"><a href="#二、消息队列核心概念" class="headerlink" title="二、消息队列核心概念"></a>二、消息队列核心概念</h2><h3 id="2-1-消息队列架构图"><a href="#2-1-消息队列架构图" class="headerlink" title="2.1 消息队列架构图"></a>2.1 消息队列架构图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🏗️ 消息队列架构"] --> B["📤 Producer\n生产者"]    A --> C["📮 Broker\n消息中间件"]    A --> D["📥 Consumer\n消费者"]    A --> E["🗂️ Queue/Topic\n存储队列"]        B -->|"发送消息"| C    C -->|"存储消息"| E    E -->|"消费消息"| D        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#c8e6c9    style E fill:#fff3e0</pre></div><h3 id="2-2-核心概念详解"><a href="#2-2-核心概念详解" class="headerlink" title="2.2 核心概念详解"></a>2.2 核心概念详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📮 MQ 核心概念"] --> B["📤 Producer\n生产者"]    A --> C["📥 Consumer\n消费者"]    A --> D["📬 Broker\n消息服务器"]    A --> E["📁 Queue\n消息队列"]    A --> F["📰 Topic\n消息主题"]    A --> G["🔄 Message\n消息"]        B --> B1["产生消息\n发送消息"]        C --> C1["订阅消息\n处理消息"]        D --> D1["接收存储\n转发消息"]        E --> E1["FIFO 队列\n点对点"]        F --> F1["发布订阅\n广播模式"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="2-3-消息模型对比"><a href="#2-3-消息模型对比" class="headerlink" title="2.3 消息模型对比"></a>2.3 消息模型对比</h3><p><strong>点对点模型（Point-to-Point）：</strong></p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📤 生产者"] --> B["📁 Queue"]    B --> C1["消费者1"]    B --> C2["消费者2"]    B --> C3["消费者3"]        style B fill:#fff3e0    style C1 fill:#c8e6c9    style C2 fill:#c8e6c9    style C3 fill:#c8e6c9</pre></div><ul><li>消息只能被<strong>一个消费者消费</strong></li><li>消息消费后从队列中删除</li><li>典型实现：ActiveMQ、RabbitMQ（Queue）</li></ul><p><strong>发布订阅模型（Publish-Subscribe）：</strong></p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📤 生产者"] --> B["📰 Topic\n消息主题"]    B --> C1["消费者1"]    B --> C2["消费者2"]    B --> C3["消费者3"]        style B fill:#f8bbd0    style C1 fill:#c8e6c9    style C2 fill:#c8e6c9    style C3 fill:#c8e6c9</pre></div><ul><li>消息可以被<strong>所有订阅的消费者消费</strong></li><li>消息消费后仍然保留（可配置）</li><li>典型实现：Kafka、RocketMQ（Topic）</li></ul><h3 id="2-4-消息消费模式"><a href="#2-4-消息消费模式" class="headerlink" title="2.4 消息消费模式"></a>2.4 消息消费模式</h3><table><thead><tr><th>模式</th><th>说明</th><th>特点</th></tr></thead><tbody><tr><td><strong>推模式（Push）</strong></td><td>Broker 主动推送消息给消费者</td><td>实时性高，消费者压力大</td></tr><tr><td><strong>拉模式（Pull）</strong></td><td>消费者主动从 Broker 拉取消息</td><td>消费者控制节奏，可批量处理</td></tr></tbody></table><hr><h2 id="三、主流消息队列对比"><a href="#三、主流消息队列对比" class="headerlink" title="三、主流消息队列对比"></a>三、主流消息队列对比</h2><h3 id="3-1-三大-MQ-对比"><a href="#3-1-三大-MQ-对比" class="headerlink" title="3.1 三大 MQ 对比"></a>3.1 三大 MQ 对比</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📊 主流消息队列对比"] --> B["🐰 RabbitMQ"]    A --> C["📈 Kafka"]    A --> D["🚀 RocketMQ"]        B --> B1[" Erlang 开发\n Erlang 开发"]    B1 --> B2["✅ 功能丰富\n✅ 管理界面友好\n❌ 吞吐量有限"]        C --> C1[" Scala 开发\n（ JVM 生态）"]    C1 --> C2["✅ 高吞吐量\n✅ 分布式\n❌ 功能相对简单"]        D --> D1[" Java 开发\n（阿里巴巴）"]    D1 --> D2["✅ 高吞吐\n✅ 事务消息\n✅ 延迟消息"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0</pre></div><h3 id="3-2-详细对比"><a href="#3-2-详细对比" class="headerlink" title="3.2 详细对比"></a>3.2 详细对比</h3><table><thead><tr><th>对比维度</th><th>RabbitMQ</th><th>Kafka</th><th>RocketMQ</th></tr></thead><tbody><tr><td><strong>吞吐量</strong></td><td>万级&#x2F;秒</td><td>百万级&#x2F;秒</td><td>十万级&#x2F;秒</td></tr><tr><td><strong>延迟</strong></td><td>微秒级</td><td>毫秒级</td><td>毫秒级</td></tr><tr><td><strong>消息可靠性</strong></td><td>⭐⭐⭐</td><td>⭐⭐⭐</td><td>⭐⭐⭐⭐</td></tr><tr><td><strong>事务消息</strong></td><td>支持</td><td>支持</td><td>原生支持</td></tr><tr><td><strong>延迟消息</strong></td><td>支持</td><td>不支持</td><td>原生支持</td></tr><tr><td><strong>顺序消息</strong></td><td>支持</td><td>支持</td><td>支持</td></tr><tr><td><strong>集群模式</strong></td><td>主从</td><td>分布式</td><td>主从</td></tr><tr><td><strong>管理界面</strong></td><td>友好</td><td>一般</td><td>一般</td></tr><tr><td><strong>社区活跃度</strong></td><td>高</td><td>非常高</td><td>高</td></tr><tr><td><strong>适用场景</strong></td><td>业务消息</td><td>日志、大数据</td><td>电商交易</td></tr></tbody></table><h3 id="3-3-如何选择-MQ？"><a href="#3-3-如何选择-MQ？" class="headerlink" title="3.3 如何选择 MQ？"></a>3.3 如何选择 MQ？</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🎯 MQ 选择指南"] --> B["业务消息\复杂功能"]    A --> C["日志收集\n大数据处理"]    A --> D["交易系统\n高可靠消息"]        B --> B1["🐰 RabbitMQ"]    B1 --> B2["功能丰富\n管理方便"]        C --> C1["📈 Kafka"]    C1 --> C2["高吞吐\n生态完善"]        D --> D1["🚀 RocketMQ"]    D1 --> D2["事务消息\n延迟消息"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0</pre></div><hr><h2 id="四、RabbitMQ-详解"><a href="#四、RabbitMQ-详解" class="headerlink" title="四、RabbitMQ 详解"></a>四、RabbitMQ 详解</h2><h3 id="4-1-RabbitMQ-核心概念"><a href="#4-1-RabbitMQ-核心概念" class="headerlink" title="4.1 RabbitMQ 核心概念"></a>4.1 RabbitMQ 核心概念</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🐰 RabbitMQ 核心概念"] --> B["📤 Producer\n生产者"]    A --> C["🗂️ Exchange\n交换机"]    A --> D["📁 Queue\n队列"]    A --> E["📥 Consumer\n消费者"]    A --> F["🔗 Binding\n绑定"]        B -->|"发送消息"| C    C -->|"根据路由键"| D    D -->|"消息投递"| E    B -.->|"管理"| F        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#f8bbd0    style D fill:#fff3e0    style E fill:#c8e6c9</pre></div><h3 id="4-2-Exchange-类型"><a href="#4-2-Exchange-类型" class="headerlink" title="4.2 Exchange 类型"></a>4.2 Exchange 类型</h3><table><thead><tr><th>类型</th><th>说明</th><th>路由规则</th></tr></thead><tbody><tr><td><strong>Direct</strong></td><td>精确匹配路由键</td><td>完全匹配</td></tr><tr><td><strong>Fanout</strong></td><td>广播到所有队列</td><td>忽略路由键</td></tr><tr><td><strong>Topic</strong></td><td>模糊匹配路由键</td><td>支持 * 和 #</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Exchange 类型示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. Direct Exchange：精确匹配</span></span><br><span class="line">channel.exchangeDeclare(<span class="string">&quot;direct.exchange&quot;</span>, BuiltinExchangeType.DIRECT);</span><br><span class="line">channel.queueDeclare(<span class="string">&quot;direct.queue&quot;</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">channel.queueBind(<span class="string">&quot;direct.queue&quot;</span>, <span class="string">&quot;direct.exchange&quot;</span>, <span class="string">&quot;order.created&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. Fanout Exchange：广播（忽略路由键）</span></span><br><span class="line">channel.exchangeDeclare(<span class="string">&quot;fanout.exchange&quot;</span>, BuiltinExchangeType.FANOUT);</span><br><span class="line">channel.queueDeclare(<span class="string">&quot;fanout.queue1&quot;</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">channel.queueDeclare(<span class="string">&quot;fanout.queue2&quot;</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">channel.queueBind(<span class="string">&quot;fanout.queue1&quot;</span>, <span class="string">&quot;fanout.exchange&quot;</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line">channel.queueBind(<span class="string">&quot;fanout.queue2&quot;</span>, <span class="string">&quot;fanout.exchange&quot;</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. Topic Exchange：模糊匹配</span></span><br><span class="line">channel.exchangeDeclare(<span class="string">&quot;topic.exchange&quot;</span>, BuiltinExchangeType.TOPIC);</span><br><span class="line">channel.queueBind(<span class="string">&quot;topic.queue&quot;</span>, <span class="string">&quot;topic.exchange&quot;</span>, <span class="string">&quot;order.*&quot;</span>);  <span class="comment">// order.created, order.paid</span></span><br><span class="line">channel.queueBind(<span class="string">&quot;topic.queue&quot;</span>, <span class="string">&quot;topic.exchange&quot;</span>, <span class="string">&quot;user.#&quot;</span>);    <span class="comment">// user.created, user.updated</span></span><br></pre></td></tr></table></figure><h3 id="4-3-RabbitMQ-消息确认机制"><a href="#4-3-RabbitMQ-消息确认机制" class="headerlink" title="4.3 RabbitMQ 消息确认机制"></a>4.3 RabbitMQ 消息确认机制</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["✅ RabbitMQ 消息确认"] --> B["📤 生产者确认"]    A --> C["📥 消费者确认"]        B --> B1["publisher confirm\n消息发送后确认"]    B1 --> B2["事务模式\n（性能差）"]    B1 --> B3["confirm 模式\n（推荐）"]        C --> C1["manual ack\n手动确认"]    C1 --> C2["自动 ack\n消息即达即确认"]    C1 --> C3["手动 ack\n处理完成后确认"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd</pre></div><h3 id="4-4-Spring-AMQP-集成-RabbitMQ"><a href="#4-4-Spring-AMQP-集成-RabbitMQ" class="headerlink" title="4.4 Spring AMQP 集成 RabbitMQ"></a>4.4 Spring AMQP 集成 RabbitMQ</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Maven 依赖 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-amqp<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">localhost</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">5672</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">guest</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">guest</span></span><br><span class="line">    <span class="comment"># 开启发送确认</span></span><br><span class="line">    <span class="attr">publisher-confirm-type:</span> <span class="string">correlated</span></span><br><span class="line">    <span class="comment"># 开启返回确认</span></span><br><span class="line">    <span class="attr">publisher-returns:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RabbitMQ 配置类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RabbitMQConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">EXCHANGE_NAME</span> <span class="operator">=</span> <span class="string">&quot;order.exchange&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;order.queue&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ROUTING_KEY</span> <span class="operator">=</span> <span class="string">&quot;order.created&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 定义交换机</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">orderExchange</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DirectExchange</span>(EXCHANGE_NAME);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 定义队列</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">orderQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(QUEUE_NAME)</span><br><span class="line">                .withArgument(<span class="string">&quot;x-dead-letter-exchange&quot;</span>, <span class="string">&quot;dlx.exchange&quot;</span>)  <span class="comment">// 死信队列</span></span><br><span class="line">                .withArgument(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>, <span class="string">&quot;dlx.order&quot;</span>)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 绑定队列和交换机</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">orderBinding</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(orderQueue())</span><br><span class="line">                .to(orderExchange())</span><br><span class="line">                .with(ROUTING_KEY);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-5-生产者与消费者示例"><a href="#4-5-生产者与消费者示例" class="headerlink" title="4.5 生产者与消费者示例"></a>4.5 生产者与消费者示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 消息生产者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderProducer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendOrderMessage</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="comment">// 发送消息到交换机</span></span><br><span class="line">        rabbitTemplate.convertAndSend(</span><br><span class="line">            RabbitMQConfig.EXCHANGE_NAME,</span><br><span class="line">            RabbitMQConfig.ROUTING_KEY,</span><br><span class="line">            order</span><br><span class="line">        );</span><br><span class="line">        System.out.println(<span class="string">&quot;📤 消息已发送：&quot;</span> + order);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送延迟消息（需要延迟插件）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendDelayMessage</span><span class="params">(Order order, <span class="type">int</span> delayMs)</span> &#123;</span><br><span class="line">        rabbitTemplate.convertAndSend(</span><br><span class="line">            RabbitMQConfig.EXCHANGE_NAME,</span><br><span class="line">            RabbitMQConfig.ROUTING_KEY,</span><br><span class="line">            order,</span><br><span class="line">            message -&gt; &#123;</span><br><span class="line">                message.getMessageProperties().setDelay(delayMs);</span><br><span class="line">                <span class="keyword">return</span> message;</span><br><span class="line">            &#125;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 消息消费者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 监听并消费消息</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * <span class="doctag">@QueueBinding</span>: 绑定队列</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@Exchange</span>: 声明交换机</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@Queue</span>: 声明队列</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RabbitListener(</span></span><br><span class="line"><span class="meta">        bindings = @QueueBinding(</span></span><br><span class="line"><span class="meta">            value = @Queue(value = RabbitMQConfig.QUEUE_NAME, durable = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">            exchange = @Exchange(value = RabbitMQConfig.EXCHANGE_NAME, type = ExchangeTypes.DIRECT),</span></span><br><span class="line"><span class="meta">            key = RabbitMQConfig.ROUTING_KEY</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleOrderMessage</span><span class="params">(Order order, Message message, Channel channel)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;📥 收到订单消息：&quot;</span> + order);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 模拟业务处理</span></span><br><span class="line">            processOrder(order);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 手动确认消息</span></span><br><span class="line">            channel.basicAck(message.getMessageProperties().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 消息已确认&quot;</span>);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 消息处理失败：&quot;</span> + e.getMessage());</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 拒绝消息，重新入队</span></span><br><span class="line">                channel.basicNack(message.getMessageProperties().getDeliveryTag(), <span class="literal">false</span>, <span class="literal">true</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException ioException) &#123;</span><br><span class="line">                ioException.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">processOrder</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="comment">// 业务处理逻辑</span></span><br><span class="line">        System.out.println(<span class="string">&quot;🔄 处理订单：&quot;</span> + order.getId());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、Kafka-详解"><a href="#五、Kafka-详解" class="headerlink" title="五、Kafka 详解"></a>五、Kafka 详解</h2><h3 id="5-1-Kafka-核心概念"><a href="#5-1-Kafka-核心概念" class="headerlink" title="5.1 Kafka 核心概念"></a>5.1 Kafka 核心概念</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📈 Kafka 核心概念"] --> B["📤 Producer\n生产者"]    A --> C["🗂️ Broker\n消息服务器"]    A --> D["📰 Topic\n消息主题"]    A --> E["📁 Partition\n分区"]    A --> F["📥 Consumer\n消费者"]    A --> G["📊 Consumer Group\n消费组"]        B -->|"发送消息"| D    D -->|"存储分区"| E    E -->|"消费"| F    F -->|"组成"| G        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0    style E fill:#fff3e0    style F fill:#c8e6c9    style G fill:#e3f2fd</pre></div><h3 id="5-2-Kafka-架构图"><a href="#5-2-Kafka-架构图" class="headerlink" title="5.2 Kafka 架构图"></a>5.2 Kafka 架构图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📈 Kafka 集群架构"] --> B["Zookeeper/KRaft"]    A --> C["Broker 1\n(Leader)"]    A --> D["Broker 2\n(Follower)"]    A --> E["Broker 3\n(Follower)"]        C --> F["Topic: order\nPartitions: 3"]    D --> G["Topic: order\nPartitions: 3"]    E --> H["Topic: order\nPartitions: 3"]        style A fill:#fff3e0    style C fill:#e3f2fd</pre></div><h3 id="5-3-Spring-Kafka-集成"><a href="#5-3-Spring-Kafka-集成" class="headerlink" title="5.3 Spring Kafka 集成"></a>5.3 Spring Kafka 集成</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Maven 依赖 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.kafka<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-kafka<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">kafka:</span></span><br><span class="line">    <span class="attr">bootstrap-servers:</span> <span class="string">localhost:9092</span></span><br><span class="line">    <span class="attr">producer:</span></span><br><span class="line">      <span class="attr">key-serializer:</span> <span class="string">org.apache.kafka.common.serialization.StringSerializer</span></span><br><span class="line">      <span class="attr">value-serializer:</span> <span class="string">org.apache.kafka.common.serialization.StringSerializer</span></span><br><span class="line">      <span class="attr">acks:</span> <span class="string">all</span>  <span class="comment"># 所有副本确认</span></span><br><span class="line">      <span class="attr">retries:</span> <span class="number">3</span></span><br><span class="line">    <span class="attr">consumer:</span></span><br><span class="line">      <span class="attr">group-id:</span> <span class="string">order-consumer-group</span></span><br><span class="line">      <span class="attr">auto-offset-reset:</span> <span class="string">earliest</span></span><br><span class="line">      <span class="attr">key-deserializer:</span> <span class="string">org.apache.kafka.common.serialization.StringDeserializer</span></span><br><span class="line">      <span class="attr">value-deserializer:</span> <span class="string">org.apache.kafka.common.serialization.StringDeserializer</span></span><br></pre></td></tr></table></figure><h3 id="5-4-生产者与消费者示例"><a href="#5-4-生产者与消费者示例" class="headerlink" title="5.4 生产者与消费者示例"></a>5.4 生产者与消费者示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Kafka 生产者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderKafkaProducer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> KafkaTemplate&lt;String, String&gt; kafkaTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TOPIC</span> <span class="operator">=</span> <span class="string">&quot;order-topic&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendOrder</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="comment">// 发送消息（指定 key 保证同一订单消息到同一分区）</span></span><br><span class="line">        kafkaTemplate.send(TOPIC, order.getId().toString(), JSON.toJSONString(order))</span><br><span class="line">            .whenComplete((result, ex) -&gt; &#123;</span><br><span class="line">                <span class="keyword">if</span> (ex != <span class="literal">null</span>) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;❌ 消息发送失败：&quot;</span> + ex.getMessage());</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;✅ 消息发送成功，分区：&quot;</span> + result.getRecordMetadata().partition());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送同步消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendSyncOrder</span><span class="params">(Order order)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">        kafkaTemplate.send(TOPIC, order.getId().toString(), JSON.toJSONString(order)).get();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 同步消息发送成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Kafka 消费者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderKafkaConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 消费消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@KafkaListener(topics = &quot;order-topic&quot;, groupId = &quot;order-consumer-group&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">consumeOrder</span><span class="params">(ConsumerRecord&lt;String, String&gt; record)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;📥 收到消息：partition=&quot;</span> + record.partition() </span><br><span class="line">            + <span class="string">&quot;, offset=&quot;</span> + record.offset());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> JSON.parseObject(record.value(), Order.class);</span><br><span class="line">            processOrder(order);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 消息处理失败：&quot;</span> + e.getMessage());</span><br><span class="line">            <span class="comment">// Kafka 不支持重试入队，通常需要手动处理</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 手动提交 offset</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@KafkaListener(topics = &quot;order-topic&quot;, groupId = &quot;manual-commit-group&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">consumeOrderManual</span><span class="params">(ConsumerRecord&lt;String, String&gt; record, </span></span><br><span class="line"><span class="params">                                   Acknowledgment ack)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            processOrder(JSON.parseObject(record.value(), Order.class));</span><br><span class="line">            ack.acknowledge();  <span class="comment">// 手动确认</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">// 处理失败，暂不确认，等待重试</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、RocketMQ-详解"><a href="#六、RocketMQ-详解" class="headerlink" title="六、RocketMQ 详解"></a>六、RocketMQ 详解</h2><h3 id="6-1-RocketMQ-核心概念"><a href="#6-1-RocketMQ-核心概念" class="headerlink" title="6.1 RocketMQ 核心概念"></a>6.1 RocketMQ 核心概念</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🚀 RocketMQ 核心概念"] --> B["📤 Producer\n生产者"]    A --> C["🗂️ Broker\n消息服务器"]    A --> D["📰 Topic\n消息主题"]    A --> E["📥 Consumer\n消费者"]    A --> F["📋 NameServer\n名字服务"]        B -->|"注册/查找"| F    F -->|"路由信息"| C        style A fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="6-2-RocketMQ-的独特优势"><a href="#6-2-RocketMQ-的独特优势" class="headerlink" title="6.2 RocketMQ 的独特优势"></a>6.2 RocketMQ 的独特优势</h3><table><thead><tr><th>特性</th><th>说明</th><th>RabbitMQ&#x2F;Kafka 对比</th></tr></thead><tbody><tr><td><strong>事务消息</strong></td><td>支持半消息和回查机制</td><td>RabbitMQ 支持但复杂，Kafka 不支持</td></tr><tr><td><strong>延迟消息</strong></td><td>原生支持多种延迟级别</td><td>RabbitMQ 需插件，Kafka 不支持</td></tr><tr><td><strong>顺序消息</strong></td><td>支持严格顺序</td><td>都支持</td></tr><tr><td><strong>死信队列</strong></td><td>原生支持</td><td>RabbitMQ 支持，Kafka 不原生支持</td></tr></tbody></table><h3 id="6-3-Spring-RocketMQ-集成"><a href="#6-3-Spring-RocketMQ-集成" class="headerlink" title="6.3 Spring RocketMQ 集成"></a>6.3 Spring RocketMQ 集成</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Maven 依赖 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.rocketmq<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>rocketmq-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.2.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">rocketmq:</span></span><br><span class="line">  <span class="attr">name-server:</span> <span class="string">localhost:9876</span></span><br><span class="line">  <span class="attr">producer:</span></span><br><span class="line">    <span class="attr">group:</span> <span class="string">order-producer-group</span></span><br><span class="line">    <span class="attr">send-message-timeout:</span> <span class="number">3000</span></span><br></pre></td></tr></table></figure><h3 id="6-4-事务消息示例"><a href="#6-4-事务消息示例" class="headerlink" title="6.4 事务消息示例"></a>6.4 事务消息示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RocketMQ 事务消息生产者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderTransactionProducer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RocketMQTemplate rocketMQTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TOPIC</span> <span class="operator">=</span> <span class="string">&quot;order-topic&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送事务消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendTransactionMessage</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        rocketMQTemplate.asyncSend(TOPIC + <span class="string">&quot;:transaction&quot;</span>, order, <span class="keyword">new</span> <span class="title class_">SendCallback</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onSuccess</span><span class="params">(SendResult result)</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;✅ 事务消息发送成功：&quot;</span> + result.getTransactionId());</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onException</span><span class="params">(Throwable e)</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;❌ 事务消息发送失败：&quot;</span> + e.getMessage());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 事务消息监听器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@RocketMQTransactionListener</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderTransactionListener</span> <span class="keyword">implements</span> <span class="title class_">RocketMQLocalTransactionListener</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderService orderService;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 执行本地事务（扣减库存等）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> RocketMQLocalTransactionState <span class="title function_">executeLocalTransaction</span><span class="params">(Message msg, Object arg)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> JSON.parseObject(<span class="keyword">new</span> <span class="title class_">String</span>(msg.getBody()), Order.class);</span><br><span class="line">            orderService.createOrder(order);  <span class="comment">// 本地事务</span></span><br><span class="line">            <span class="keyword">return</span> RocketMQLocalTransactionState.COMMIT;  <span class="comment">// 提交</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">return</span> RocketMQLocalTransactionState.ROLLBACK;  <span class="comment">// 回滚</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 检查本地事务状态（回查）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> RocketMQLocalTransactionState <span class="title function_">checkLocalTransaction</span><span class="params">(Message msg)</span> &#123;</span><br><span class="line">        <span class="comment">// 查询本地事务状态</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">orderId</span> <span class="operator">=</span> msg.getBody();</span><br><span class="line">        <span class="keyword">if</span> (orderService.isOrderProcessed(orderId)) &#123;</span><br><span class="line">            <span class="keyword">return</span> RocketMQLocalTransactionState.COMMIT;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> RocketMQLocalTransactionState.UNKNOWN;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RocketMQ 消费者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderService orderService;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@RocketMQMessageListener(</span></span><br><span class="line"><span class="meta">        topic = &quot;order-topic&quot;,</span></span><br><span class="line"><span class="meta">        consumerGroup = &quot;order-consumer-group&quot;,</span></span><br><span class="line"><span class="meta">        selectorExpression = &quot;transaction&quot;  // 只消费 tagged 消息</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderMessageListener</span> <span class="keyword">implements</span> <span class="title class_">RocketMQListener</span>&lt;Order&gt; &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onMessage</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;📥 收到订单消息：&quot;</span> + order);</span><br><span class="line">            orderService.notifyUser(order);  <span class="comment">// 发送通知</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、消息可靠性和事务"><a href="#七、消息可靠性和事务" class="headerlink" title="七、消息可靠性和事务"></a>七、消息可靠性和事务</h2><h3 id="7-1-消息可靠性的三个层面"><a href="#7-1-消息可靠性的三个层面" class="headerlink" title="7.1 消息可靠性的三个层面"></a>7.1 消息可靠性的三个层面</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["✅ 消息可靠性保证"] --> B["📤 生产者可靠性"]    A --> C["📮 Broker 可靠性"]    A --> D["📥 消费者可靠性"]        B --> B1["确认机制\n重试机制"]    B1 --> B2["acks=all\n事务消息"]        C --> C1["持久化\n副本机制"]    C1 --> C2["同步刷盘\n多副本同步"]        D --> D1["手动确认\n消费幂等"]    D1 --> D2["手动 ACK\n幂等处理"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0</pre></div><h3 id="7-2-如何保证不丢消息？"><a href="#7-2-如何保证不丢消息？" class="headerlink" title="7.2 如何保证不丢消息？"></a>7.2 如何保证不丢消息？</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 生产者端：确保消息不丢失</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReliableProducerConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RabbitTemplate <span class="title function_">reliableRabbitTemplate</span><span class="params">(ConnectionFactory connectionFactory)</span> &#123;</span><br><span class="line">        <span class="type">RabbitTemplate</span> <span class="variable">template</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RabbitTemplate</span>(connectionFactory);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 1. 开启确认机制</span></span><br><span class="line">        template.setConfirmCallback((correlationData, ack, cause) -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (!ack) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;❌ 消息未到达 Broker：&quot;</span> + cause);</span><br><span class="line">                <span class="comment">// 记录并重试</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 开启返回确认（消息无法路由时）</span></span><br><span class="line">        template.setReturnsCallback(returned -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 消息无法路由到队列：&quot;</span> + returned.getMessage());</span><br><span class="line">        &#125;);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> template;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 消费者端：确保消息被正确处理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReliableConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;order.queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleMessage</span><span class="params">(Order order, Channel channel, </span></span><br><span class="line"><span class="params">                            <span class="meta">@Header(AmqpHeaders.DELIVERY_TAG)</span> <span class="type">long</span> deliveryTag)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 1. 业务处理</span></span><br><span class="line">            processOrder(order);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 2. 手动确认</span></span><br><span class="line">            channel.basicAck(deliveryTag, <span class="literal">false</span>);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">// 3. 业务处理失败，拒绝消息</span></span><br><span class="line">            <span class="comment">// requeue=false 表示不重新入队，进入死信队列</span></span><br><span class="line">            channel.basicNack(deliveryTag, <span class="literal">false</span>, <span class="literal">false</span>);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 或者 requeue=true 重新入队重试</span></span><br><span class="line">            <span class="comment">// channel.basicNack(deliveryTag, false, true);</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-3-消息幂等性处理"><a href="#7-3-消息幂等性处理" class="headerlink" title="7.3 消息幂等性处理"></a>7.3 消息幂等性处理</h3><p>消息重复消费是分布式系统中常见问题，需要消费者实现<strong>幂等性</strong>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 消费者端幂等性处理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IdempotentConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate&lt;String, String&gt; redisTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;order.queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleOrder</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;order:processed:&quot;</span> + order.getId();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 1. Redis 分布式锁 + 防重表</span></span><br><span class="line">        <span class="type">Boolean</span> <span class="variable">locked</span> <span class="operator">=</span> redisTemplate.opsForValue()</span><br><span class="line">            .setIfAbsent(key, <span class="string">&quot;1&quot;</span>, Duration.ofHours(<span class="number">24</span>));</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (Boolean.FALSE.equals(locked)) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;⏭️ 订单已处理，跳过：&quot;</span> + order.getId());</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            processOrder(order);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            redisTemplate.delete(key);  <span class="comment">// 失败时释放锁</span></span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 数据库唯一索引实现幂等</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">createOrderWithIdempotent</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="comment">// 使用订单 ID 作为唯一索引</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            orderMapper.insertSelective(order);  <span class="comment">// 重复插入会抛异常</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (DuplicateKeyException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;⏭️ 订单已存在，跳过：&quot;</span> + order.getId());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-4-消息顺序处理"><a href="#7-4-消息顺序处理" class="headerlink" title="7.4 消息顺序处理"></a>7.4 消息顺序处理</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 保证消息顺序处理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. Kafka：使用相同 key 发送到同一分区</span></span><br><span class="line">kafkaTemplate.send(TOPIC, order.getUserId().toString(), orderJSON);</span><br><span class="line"><span class="comment">// 同一用户的订单会在同一分区，按 offset 顺序消费</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. RabbitMQ：使用单消费者 + 内部队列</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> SimpleRabbitListenerContainerFactory <span class="title function_">rabbitListenerContainerFactory</span><span class="params">(</span></span><br><span class="line"><span class="params">        ConnectionFactory connectionFactory)</span> &#123;</span><br><span class="line">    <span class="type">SimpleRabbitListenerContainerFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleRabbitListenerContainerFactory</span>();</span><br><span class="line">    factory.setConnectionFactory(connectionFactory);</span><br><span class="line">    factory.setConcurrent(<span class="number">1</span>);  <span class="comment">// 单线程消费，保证顺序</span></span><br><span class="line">    factory.setAcknowledgeMode(AcknowledgeMode.AUTO);</span><br><span class="line">    <span class="keyword">return</span> factory;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、Spring-Boot-集成-MQ"><a href="#八、Spring-Boot-集成-MQ" class="headerlink" title="八、Spring Boot 集成 MQ"></a>八、Spring Boot 集成 MQ</h2><h3 id="8-1-RabbitMQ-Spring-Boot-完整示例"><a href="#8-1-RabbitMQ-Spring-Boot-完整示例" class="headerlink" title="8.1 RabbitMQ + Spring Boot 完整示例"></a>8.1 RabbitMQ + Spring Boot 完整示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 订单服务：整合 RabbitMQ</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建订单并发送消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">createOrder</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 保存订单</span></span><br><span class="line">        order.setStatus(OrderStatus.PENDING);</span><br><span class="line">        orderMapper.insert(order);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 发送消息通知其他系统</span></span><br><span class="line">        rabbitTemplate.convertAndSend(</span><br><span class="line">            <span class="string">&quot;order.exchange&quot;</span>,</span><br><span class="line">            <span class="string">&quot;order.created&quot;</span>,</span><br><span class="line">            order</span><br><span class="line">        );</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 订单创建成功，消息已发送：&quot;</span> + order.getId());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 库存服务：监听订单消息</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InventoryService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;inventory.queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleOrderCreated</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;📥 收到订单消息：&quot;</span> + order.getId());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 扣减库存</span></span><br><span class="line">        inventoryMapper.deduct(order.getProductId(), order.getQuantity());</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 库存扣减成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通知服务：监听订单消息</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NotificationService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;notification.queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleOrderCreated</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;📥 收到订单消息：&quot;</span> + order.getId());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 发送短信通知</span></span><br><span class="line">        smsService.sendOrderNotification(order);</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 通知发送成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-2-MQ-配置类"><a href="#8-2-MQ-配置类" class="headerlink" title="8.2 MQ 配置类"></a>8.2 MQ 配置类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * MQ 完整配置类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MQConfiguration</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==================== 交换机定义 ====================</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">orderExchange</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> ExchangeBuilder.directExchange(<span class="string">&quot;order.exchange&quot;</span>)</span><br><span class="line">                .durable(<span class="literal">true</span>)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==================== 队列定义 ====================</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">orderQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(<span class="string">&quot;order.queue&quot;</span>)</span><br><span class="line">                .withArgument(<span class="string">&quot;x-dead-letter-exchange&quot;</span>, <span class="string">&quot;dlx.exchange&quot;</span>)</span><br><span class="line">                .withArgument(<span class="string">&quot;x-dead-letter-routing-key&quot;</span>, <span class="string">&quot;dlx.order&quot;</span>)</span><br><span class="line">                .withArgument(<span class="string">&quot;x-message-ttl&quot;</span>, <span class="number">86400000</span>)  <span class="comment">// 24小时过期</span></span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">inventoryQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(<span class="string">&quot;inventory.queue&quot;</span>).build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">notificationQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(<span class="string">&quot;notification.queue&quot;</span>).build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==================== 绑定定义 ====================</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">orderInventoryBinding</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(inventoryQueue())</span><br><span class="line">                .to(orderExchange())</span><br><span class="line">                .with(<span class="string">&quot;order.created&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">orderNotificationBinding</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(notificationQueue())</span><br><span class="line">                .to(orderExchange())</span><br><span class="line">                .with(<span class="string">&quot;order.created&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==================== 死信队列 ====================</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> DirectExchange <span class="title function_">dlxExchange</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DirectExchange</span>(<span class="string">&quot;dlx.exchange&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">dlxQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> QueueBuilder.durable(<span class="string">&quot;dlx.queue&quot;</span>).build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">dlxBinding</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with(<span class="string">&quot;dlx.order&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、常见问题与最佳实践"><a href="#九、常见问题与最佳实践" class="headerlink" title="九、常见问题与最佳实践"></a>九、常见问题与最佳实践</h2><h3 id="9-1-常见问题与解决方案"><a href="#9-1-常见问题与解决方案" class="headerlink" title="9.1 常见问题与解决方案"></a>9.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["📭 消息丢失"]    A --> C["🔄 消息重复"]    A --> D["⏰ 消息延迟"]    A --> E["💥 消费积压"]        B --> B1["开启确认机制\n持久化消息"]        C --> C1["实现幂等性\n防重处理"]        D --> D1["优化消费逻辑\n增加消费者"]        E --> E1["增加消费者\n优化网络"]        style A fill:#fff3e0</pre></div><h3 id="9-2-MQ-使用最佳实践"><a href="#9-2-MQ-使用最佳实践" class="headerlink" title="9.2 MQ 使用最佳实践"></a>9.2 MQ 使用最佳实践</h3><table><thead><tr><th>实践</th><th>说明</th><th>推荐程度</th></tr></thead><tbody><tr><td><strong>消息持久化</strong></td><td>开启消息持久化，防止 Broker 宕机丢失</td><td>✅✅✅</td></tr><tr><td><strong>生产者确认</strong></td><td>开启 publisher confirm</td><td>✅✅✅</td></tr><tr><td><strong>消费者确认</strong></td><td>手动 ACK，避免消息丢失</td><td>✅✅✅</td></tr><tr><td><strong>消费幂等</strong></td><td>消费者实现幂等处理</td><td>✅✅✅</td></tr><tr><td><strong>死信队列</strong></td><td>配置死信队列处理失败消息</td><td>✅✅</td></tr><tr><td><strong>消息压缩</strong></td><td>大消息考虑压缩</td><td>✅✅</td></tr><tr><td><strong>延迟队列</strong></td><td>定时任务使用延迟消息</td><td>✅✅</td></tr></tbody></table><h3 id="9-3-MQ-选型建议"><a href="#9-3-MQ-选型建议" class="headerlink" title="9.3 MQ 选型建议"></a>9.3 MQ 选型建议</h3><table><thead><tr><th>场景</th><th>推荐 MQ</th><th>原因</th></tr></thead><tbody><tr><td><strong>电商交易系统</strong></td><td>RocketMQ</td><td>原生事务消息、延迟消息</td></tr><tr><td><strong>日志收集系统</strong></td><td>Kafka</td><td>高吞吐、日志生态完善</td></tr><tr><td><strong>企业应用消息</strong></td><td>RabbitMQ</td><td>功能丰富、管理界面友好</td></tr><tr><td><strong>短信通知系统</strong></td><td>RocketMQ</td><td>支持延迟消息</td></tr><tr><td><strong>大数据实时计算</strong></td><td>Kafka</td><td>高吞吐、分布式</td></tr></tbody></table><hr><h2 id="十、总结"><a href="#十、总结" class="headerlink" title="十、总结"></a>十、总结</h2><h3 id="10-1-核心知识点回顾"><a href="#10-1-核心知识点回顾" class="headerlink" title="10.1 核心知识点回顾"></a>10.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((消息队列 MQ))    核心概念      Producer 生产者      Consumer 消费者      Broker 服务器      Queue 队列      Topic 主题    消息模型      点对点模式      发布订阅模式    RabbitMQ      Exchange 交换机      路由键绑定      确认机制    Kafka      分区副本      消费组      高吞吐    RocketMQ      事务消息      延迟消息      NameServer    可靠性保证      生产者确认      Broker 持久化      消费者确认      幂等处理</pre></div><h3 id="10-2-学习路线建议"><a href="#10-2-学习路线建议" class="headerlink" title="10.2 学习路线建议"></a>10.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["MQ 学习路线"] --> B["第一阶段\n基础概念"]    B --> C["第二阶段\nRabbitMQ"]    C --> D["第三阶段\nKafka"]    D --> E["第四阶段\nRocketMQ"]        B --> B1["核心概念\n消息模型"]    C --> C1["Exchange\n队列绑定\n确认机制"]    D --> D1["分区副本\n消费组\n高吞吐"]    E --> E1["事务消息\n延迟消息"]        style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><hr><blockquote><p>💡 <strong>写给读者的话</strong>：消息队列是分布式系统中不可或缺的基础设施。掌握 MQ 的核心概念和实战技能，对于后端开发者来说至关重要。希望本文能帮助你建立完整的 MQ 知识体系，在项目中游刃有余地使用消息队列！📮</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/mq-guide/</id>
    <link href="https://blog.codenav.top/mq-guide/"/>
    <published>2026-05-24T11:28:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="消息队列（MQ）详解：从入门到实战-📮"><a href="#消息队列（MQ）详解：从入门到实战-📮" class="headerlink" title="消息队列（MQ）详解：从入门到实战 📮"></a>消息队列（MQ）详解：从入门到实战 📮</h1><]]>
    </summary>
    <title>消息队列（MQ）详解：从入门到实战 📮</title>
    <updated>2026-05-24T11:31:23.708Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="Spring Cloud" scheme="https://blog.codenav.top/tags/Spring-Cloud/"/>
    <category term="微服务" scheme="https://blog.codenav.top/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    <category term="分布式" scheme="https://blog.codenav.top/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    <category term="云原生" scheme="https://blog.codenav.top/tags/%E4%BA%91%E5%8E%9F%E7%94%9F/"/>
    <content>
      <![CDATA[<h1 id="Spring-Cloud-微服务架构详解：从入门到实战-☁️"><a href="#Spring-Cloud-微服务架构详解：从入门到实战-☁️" class="headerlink" title="Spring Cloud 微服务架构详解：从入门到实战 ☁️"></a>Spring Cloud 微服务架构详解：从入门到实战 ☁️</h1><blockquote><p>Spring Cloud 是微服务架构的完整解决方案，它将众多分布式开发中需要用到的组件整合在一起，为开发者提供了构建微服务架构应用的工具箱。本文将带你系统学习 Spring Cloud 的核心组件、架构设计以及实战应用！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%A6%82%E8%BF%B0%E4%BB%80%E4%B9%88%E6%98%AF%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84">一、微服务概述：什么是微服务架构？</a></li><li><a href="#%E4%BA%8Cspring-cloud-%E7%94%9F%E6%80%81%E7%B3%BB%E7%BB%9F">二、Spring Cloud 生态系统</a></li><li><a href="#%E4%B8%89%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0nacos">三、服务注册与发现：Nacos</a></li><li><a href="#%E5%9B%9B%E6%9C%8D%E5%8A%A1%E8%B0%83%E7%94%A8openfeign">四、服务调用：OpenFeign</a></li><li><a href="#%E4%BA%94%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1ribbon">五、负载均衡：Ribbon</a></li><li><a href="#%E5%85%AD%E7%BD%91%E5%85%B3%E6%9C%8D%E5%8A%A1spring-cloud-gateway">六、网关服务：Spring Cloud Gateway</a></li><li><a href="#%E4%B8%83%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83spring-cloud-config">七、配置中心：Spring Cloud Config</a></li><li><a href="#%E5%85%AB%E6%9C%8D%E5%8A%A1%E7%86%94%E6%96%ADsentinel">八、服务熔断：Sentinel</a></li><li><a href="#%E4%B9%9D%E6%B6%88%E6%81%AF%E9%A9%B1%E5%8A%A8spring-cloud-stream">九、消息驱动：Spring Cloud Stream</a></li><li><a href="#%E5%8D%81%E5%BE%AE%E6%9C%8D%E5%8A%A1%E5%AE%9E%E6%88%98%E5%AE%8C%E6%95%B4%E9%A1%B9%E7%9B%AE%E6%9E%84%E5%BB%BA">十、微服务实战：完整项目构建</a></li><li><a href="#%E5%8D%81%E4%B8%80%E5%BE%AE%E6%9C%8D%E5%8A%A1%E5%AE%89%E5%85%A8spring-cloud-security">十一、微服务安全：Spring Cloud Security</a></li><li><a href="#%E5%8D%81%E4%BA%8C%E6%80%BB%E7%BB%93%E4%B8%8E%E5%B1%95%E6%9C%9B">十二、总结与展望</a></li></ul><hr><h2 id="一、微服务概述：什么是微服务架构？"><a href="#一、微服务概述：什么是微服务架构？" class="headerlink" title="一、微服务概述：什么是微服务架构？"></a>一、微服务概述：什么是微服务架构？</h2><h3 id="1-1-从单体架构到微服务"><a href="#1-1-从单体架构到微服务" class="headerlink" title="1.1 从单体架构到微服务"></a>1.1 从单体架构到微服务</h3><p>在微服务诞生之前，传统的应用大多采用<strong>单体架构</strong>（Monolithic Architecture）。随着业务规模的增长，单体架构的局限性逐渐显现。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🏗️ 架构演进"] --> B["📦 单体架构"]    B --> B1["所有模块\n打包在一个应用"]    B1 --> B2["❌ 部署困难\n扩展性差"]    B2 --> B3["❌ 技术栈单一\n团队协作难"]        A --> C["🔄 微服务架构"]    C --> C1["每个模块\n独立部署"]    C1 --> C2["✅ 独立扩展\n技术多样"]    C2 --> C3["✅ 团队自治\n快速迭代"]        style B fill:#ffcdd2    style C fill:#c8e6c9</pre></div><h3 id="1-2-微服务的核心特征"><a href="#1-2-微服务的核心特征" class="headerlink" title="1.2 微服务的核心特征"></a>1.2 微服务的核心特征</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["✨ 微服务核心特征"] --> B["🏷️ 服务单一职责"]    A --> C["🔗 轻量级通信"]    A --> D["📦 独立部署"]    A --> E["🧩 去中心化管理"]    A --> F["🔍 基础设施自动化"]        B --> B1["每个服务只做好\n一件事"]        C --> C1["HTTP API\n消息队列"]        D --> D1["独立进程\n独立数据库"]        E --> E1["技术选型自由\n数据自主管理"]        F --> F1["CI/CD\n容器化"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="1-3-微服务带来的挑战"><a href="#1-3-微服务带来的挑战" class="headerlink" title="1.3 微服务带来的挑战"></a>1.3 微服务带来的挑战</h3><table><thead><tr><th>挑战</th><th>说明</th><th>解决方案</th></tr></thead><tbody><tr><td><strong>服务治理</strong></td><td>服务如何被发现和调用</td><td>Nacos、Eureka</td></tr><tr><td><strong>负载均衡</strong></td><td>如何分配请求</td><td>Ribbon、LoadBalancer</td></tr><tr><td><strong>网关路由</strong></td><td>统一入口和权限</td><td>Gateway</td></tr><tr><td><strong>配置管理</strong></td><td>配置文件分散如何管理</td><td>Config、Nacos</td></tr><tr><td><strong>服务熔断</strong></td><td>单个服务故障如何处理</td><td>Sentinel、Hystrix</td></tr><tr><td><strong>分布式事务</strong></td><td>跨服务数据一致性</td><td>Seata</td></tr><tr><td><strong>日志追踪</strong></td><td>分布式日志如何聚合</td><td>Sleuth、Zipkin</td></tr></tbody></table><hr><h2 id="二、Spring-Cloud-生态系统"><a href="#二、Spring-Cloud-生态系统" class="headerlink" title="二、Spring Cloud 生态系统"></a>二、Spring Cloud 生态系统</h2><h3 id="2-1-Spring-Cloud-组件全景图"><a href="#2-1-Spring-Cloud-组件全景图" class="headerlink" title="2.1 Spring Cloud 组件全景图"></a>2.1 Spring Cloud 组件全景图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["☁️ Spring Cloud 生态"] --> B["🛤️ 服务注册发现"]    A --> C["🚪 API 网关"]    A --> D["⚙️ 配置中心"]    A --> E["💥 服务熔断"]    A --> F["📡 服务调用"]    A --> G["📝 日志追踪"]    A --> H["🔐 安全认证"]        B --> B1["Nacos\nEureka\nConsul"]        C --> C1["Gateway\nZuul"]        D --> D1["Nacos Config\nSpring Cloud Config"]        E --> E1["Sentinel\nSentinel"]        F --> F1["OpenFeign\nRestTemplate"]        G --> G1["Sleuth\nZipkin"]        H --> H1["Spring Security\nOAuth2"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0    style G fill:#e3f2fd    style H fill:#fff3e0</pre></div><h3 id="2-2-版本对应关系"><a href="#2-2-版本对应关系" class="headerlink" title="2.2 版本对应关系"></a>2.2 版本对应关系</h3><table><thead><tr><th>Spring Boot 版本</th><th>Spring Cloud 版本</th><th>主要特性</th></tr></thead><tbody><tr><td><strong>3.2.x</strong></td><td>2023.0.x</td><td>最新稳定版</td></tr><tr><td><strong>3.1.x</strong></td><td>2022.0.x</td><td>Anniversary</td></tr><tr><td><strong>3.0.x</strong></td><td>2022.0.0</td><td>重大更新</td></tr><tr><td><strong>2.7.x</strong></td><td>2021.0.x</td><td>Kilburn</td></tr><tr><td><strong>2.6.x</strong></td><td>2021.0.3</td><td>稳定版</td></tr></tbody></table><blockquote><p>💡 <strong>推荐</strong>：新项目使用 Spring Boot 3.x + Spring Cloud 2023.0.x，享受最新特性和性能优化。</p></blockquote><h3 id="2-3-常见微服务架构模式"><a href="#2-3-常见微服务架构模式" class="headerlink" title="2.3 常见微服务架构模式"></a>2.3 常见微服务架构模式</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🏗️ 微服务架构模式"] --> B["🛡️ 安全模式"]    A --> C["📊 日志追踪模式"]    A --> D["⚡ 高可用模式"]    A --> E["📈 可扩展模式"]        style A fill:#fff3e0</pre></div><hr><h2 id="三、服务注册与发现：Nacos"><a href="#三、服务注册与发现：Nacos" class="headerlink" title="三、服务注册与发现：Nacos"></a>三、服务注册与发现：Nacos</h2><h3 id="3-1-Nacos-简介"><a href="#3-1-Nacos-简介" class="headerlink" title="3.1 Nacos 简介"></a>3.1 Nacos 简介</h3><p>Nacos（Naming and Configuration Service）是阿里巴巴开源的项目，专注于<strong>服务注册与发现</strong>和<strong>配置管理</strong>。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🛤️ Nacos 核心功能"] --> B["📋 服务注册发现"]    A --> C["⚙️ 配置管理"]    A --> D["📊 命名服务"]    A --> E["🔄 动态配置更新"]        B --> B1["提供服务注册\n健康检查\n服务订阅"]        style A fill:#fff3e0    style B fill:#c8e6c9</pre></div><h3 id="3-2-Nacos-服务端安装"><a href="#3-2-Nacos-服务端安装" class="headerlink" title="3.2 Nacos 服务端安装"></a>3.2 Nacos 服务端安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 下载 Nacos</span></span><br><span class="line"><span class="comment"># https://github.com/alibaba/nacos/releases</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 解压并启动（单机模式）</span></span><br><span class="line">tar -xvf nacos-server-2.3.0.tar.gz</span><br><span class="line"><span class="built_in">cd</span> nacos/bin</span><br><span class="line"></span><br><span class="line"><span class="comment"># Linux/Mac 启动</span></span><br><span class="line">sh startup.sh -m standalone</span><br><span class="line"></span><br><span class="line"><span class="comment"># Windows 启动</span></span><br><span class="line">cmd startup.cmd -m standalone</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 访问控制台</span></span><br><span class="line"><span class="comment"># http://localhost:8848/nacos</span></span><br><span class="line"><span class="comment"># 默认用户名/密码：nacos/nacos</span></span><br></pre></td></tr></table></figure><h3 id="3-3-服务提供者配置"><a href="#3-3-服务提供者配置" class="headerlink" title="3.3 服务提供者配置"></a>3.3 服务提供者配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># provider-service/application.yml</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8081</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">provider-service</span>  <span class="comment"># 服务名称</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">server-addr:</span> <span class="string">localhost:8848</span>  <span class="comment"># Nacos 地址</span></span><br><span class="line">        <span class="attr">namespace:</span> <span class="string">public</span>           <span class="comment"># 命名空间</span></span><br><span class="line">        <span class="attr">group:</span> <span class="string">DEFAULT_GROUP</span>      <span class="comment"># 分组</span></span><br><span class="line">      <span class="attr">config:</span></span><br><span class="line">        <span class="attr">server-addr:</span> <span class="string">localhost:8848</span>  <span class="comment"># 配置中心地址</span></span><br><span class="line">        <span class="attr">file-extension:</span> <span class="string">yaml</span>        <span class="comment"># 配置文件格式</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 服务元数据</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">version:</span> <span class="string">v1</span></span><br><span class="line">  <span class="attr">env:</span> <span class="string">test</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 服务提供者启动类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span>  <span class="comment">// 启用服务发现</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProviderApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ProviderApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 服务提供者 Controller</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/provider&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProviderController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;spring.application.name&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String serviceName;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(<span class="meta">@RequestParam</span> String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello &quot;</span> + name + <span class="string">&quot;, this is &quot;</span> + serviceName;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/user/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUser</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">User</span>(id, <span class="string">&quot;User&quot;</span> + id, <span class="number">25</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-服务消费者配置"><a href="#3-4-服务消费者配置" class="headerlink" title="3.4 服务消费者配置"></a>3.4 服务消费者配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># consumer-service/application.yml</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8082</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">consumer-service</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">server-addr:</span> <span class="string">localhost:8848</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 服务消费者启动类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConsumerApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ConsumerApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用 RestTemplate + <span class="doctag">@LoadBalanced</span> 调用服务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RestTemplateConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@LoadBalanced</span>  <span class="comment">// 启用负载均衡</span></span><br><span class="line">    <span class="keyword">public</span> RestTemplate <span class="title function_">restTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 服务消费者 Controller</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/consumer&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConsumerController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RestTemplate restTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过服务名调用服务</span></span><br><span class="line"><span class="comment">     * 格式：http://&#123;服务名&#125;/&#123;路径&#125;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/call-provider&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">callProvider</span><span class="params">(<span class="meta">@RequestParam</span> String name)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;http://provider-service/provider/hello?name=&quot;</span> + name;</span><br><span class="line">        <span class="keyword">return</span> restTemplate.getForObject(url, String.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、服务调用：OpenFeign"><a href="#四、服务调用：OpenFeign" class="headerlink" title="四、服务调用：OpenFeign"></a>四、服务调用：OpenFeign</h2><h3 id="4-1-OpenFeign-简介"><a href="#4-1-OpenFeign-简介" class="headerlink" title="4.1 OpenFeign 简介"></a>4.1 OpenFeign 简介</h3><p>OpenFeign 是 Spring Cloud 对 Feign 的封装，提供了<strong>声明式 REST 客户端</strong>，让服务调用像调用本地方法一样简单。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🔗 OpenFeign 调用流程"] --> B["定义接口"]    B --> C["@FeignClient"]    C --> D["自动调用"]    D --> E["返回结果"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9</pre></div><h3 id="4-2-快速入门"><a href="#4-2-快速入门" class="headerlink" title="4.2 快速入门"></a>4.2 快速入门</h3><p><strong>1. 添加依赖</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-openfeign<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>2. 启用 Feign</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableFeignClients</span>  <span class="comment">// 启用 Feign 客户端</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConsumerApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ConsumerApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>3. 定义 Feign 客户端接口</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义 Feign 客户端接口</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@FeignClient</span> 注解属性说明：</span></span><br><span class="line"><span class="comment"> * - name/value: 服务名称（必填）</span></span><br><span class="line"><span class="comment"> * - url: 服务地址（可选，用于直接调用）</span></span><br><span class="line"><span class="comment"> * - fallback: 降级处理类</span></span><br><span class="line"><span class="comment"> * - configuration: Feign 配置类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@FeignClient(name = &quot;provider-service&quot;, fallback = ProviderFeignClientFallback.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ProviderFeignClient</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 调用的目标方法</span></span><br><span class="line"><span class="comment">     * 方法签名必须与 Provider 端一致</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/provider/hello&quot;)</span></span><br><span class="line">    String <span class="title function_">hello</span><span class="params">(<span class="meta">@RequestParam(&quot;name&quot;)</span> String name)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/provider/user/&#123;id&#125;&quot;)</span></span><br><span class="line">    User <span class="title function_">getUser</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@PostMapping(&quot;/provider/user&quot;)</span></span><br><span class="line">    User <span class="title function_">createUser</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>4. 降级处理类</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Feign 客户端降级处理类</span></span><br><span class="line"><span class="comment"> * 当 Provider 服务不可用时，会自动调用降级方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProviderFeignClientFallback</span> <span class="keyword">implements</span> <span class="title class_">ProviderFeignClient</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Sorry, &quot;</span> + name + <span class="string">&quot;, the service is unavailable! 😢&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUser</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">User</span>(id, <span class="string">&quot;Default User&quot;</span>, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">createUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>5. 使用 Feign 客户端</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/feign&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FeignController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ProviderFeignClient providerFeignClient;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(<span class="meta">@RequestParam</span> String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> providerFeignClient.hello(name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-3-OpenFeign-高级配置"><a href="#4-3-OpenFeign-高级配置" class="headerlink" title="4.3 OpenFeign 高级配置"></a>4.3 OpenFeign 高级配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">feign:</span></span><br><span class="line">  <span class="comment"># 启用 Hystrix 熔断器</span></span><br><span class="line">  <span class="attr">hystrix:</span></span><br><span class="line">    <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">  <span class="comment"># 日志级别</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">config:</span></span><br><span class="line">      <span class="attr">default:</span></span><br><span class="line">        <span class="attr">loggerLevel:</span> <span class="string">basic</span>  <span class="comment"># none/basic/headers/full</span></span><br><span class="line">  <span class="comment"># 连接超时</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">config:</span></span><br><span class="line">      <span class="attr">default:</span></span><br><span class="line">        <span class="attr">connectTimeout:</span> <span class="number">5000</span></span><br><span class="line">        <span class="attr">readTimeout:</span> <span class="number">5000</span></span><br><span class="line">  <span class="comment"># 启用压缩</span></span><br><span class="line">  <span class="attr">compression:</span></span><br><span class="line">    <span class="attr">request:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">response:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><hr><h2 id="五、负载均衡：Ribbon"><a href="#五、负载均衡：Ribbon" class="headerlink" title="五、负载均衡：Ribbon"></a>五、负载均衡：Ribbon</h2><h3 id="5-1-Ribbon-简介"><a href="#5-1-Ribbon-简介" class="headerlink" title="5.1 Ribbon 简介"></a>5.1 Ribbon 简介</h3><p>Ribbon 是 Netflix 开源的客户端负载均衡器，Spring Cloud 将其封装并与 RestTemplate 和 Feign 集成。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["⚖️ 负载均衡策略"] --> B["轮询\nRoundRobin"]    A --> C["随机\nRandom"]    A --> D["加权\nWeighted"]    A --> E["重试\nRetry"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><h3 id="5-2-负载均衡策略详解"><a href="#5-2-负载均衡策略详解" class="headerlink" title="5.2 负载均衡策略详解"></a>5.2 负载均衡策略详解</h3><table><thead><tr><th>策略</th><th>类名</th><th>说明</th></tr></thead><tbody><tr><td><strong>轮询</strong></td><td>RoundRobinRule</td><td>依次选择每个服务器</td></tr><tr><td><strong>随机</strong></td><td>RandomRule</td><td>随机选择一个服务器</td></tr><tr><td><strong>权重</strong></td><td>WeightedResponseTimeRule</td><td>根据响应时间分配权重</td></tr><tr><td><strong>最少连接</strong></td><td>BestAvailableRule</td><td>选择连接数最少的服务器</td></tr><tr><td><strong>重试</strong></td><td>RetryRule</td><td>轮询失败时重试</td></tr></tbody></table><h3 id="5-3-Ribbon-配置"><a href="#5-3-Ribbon-配置" class="headerlink" title="5.3 Ribbon 配置"></a>5.3 Ribbon 配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义 Ribbon 配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RibbonConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> IRule <span class="title function_">ribbonRule</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 方式一：使用随机策略</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RandomRule</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 方式二：使用重试策略</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RetryRule</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 方式三：使用权重策略</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">WeightedResponseTimeRule</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 为特定服务指定 Ribbon 策略</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@RibbonClient(name = &quot;provider-service&quot;, configuration = RibbonConfig.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProviderRibbonConfig</span> &#123;</span><br><span class="line">    <span class="comment">// 为 provider-service 指定自定义配置</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、网关服务：Spring-Cloud-Gateway"><a href="#六、网关服务：Spring-Cloud-Gateway" class="headerlink" title="六、网关服务：Spring Cloud Gateway"></a>六、网关服务：Spring Cloud Gateway</h2><h3 id="6-1-Gateway-简介"><a href="#6-1-Gateway-简介" class="headerlink" title="6.1 Gateway 简介"></a>6.1 Gateway 简介</h3><p>Spring Cloud Gateway 是 Spring Cloud 的第二代网关，<strong>基于 WebFlux 的响应式编程</strong>，性能比 Zuul 优秀很多。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🚪 网关核心功能"] --> B["🌐 路由转发"]    A --> C["🔐 身份认证"]    A --> D["⚡ 限流熔断"]    A --> E["📊 日志监控"]    A --> F["🎭 过滤器"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="6-2-快速入门"><a href="#6-2-快速入门" class="headerlink" title="6.2 快速入门"></a>6.2 快速入门</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gateway-service/application.yml</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8080</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">gateway-service</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">server-addr:</span> <span class="string">localhost:8848</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="comment"># 路由配置</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="comment"># 路由 ID（唯一标识）</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">provider-route</span></span><br><span class="line">          <span class="comment"># 目标服务地址</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">lb://provider-service</span></span><br><span class="line">          <span class="comment"># 断言条件（路由规则）</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Path=/provider/**</span></span><br><span class="line">          <span class="comment"># 过滤器</span></span><br><span class="line">          <span class="attr">filters:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">StripPrefix=1</span>  <span class="comment"># 去掉第一层路径</span></span><br><span class="line">          </span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">consumer-route</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">lb://consumer-service</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Path=/consumer/**</span></span><br><span class="line">          <span class="attr">filters:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">StripPrefix=1</span></span><br><span class="line">      </span><br><span class="line">      <span class="comment"># 全局跨域配置</span></span><br><span class="line">      <span class="attr">globalcors:</span></span><br><span class="line">        <span class="attr">corsConfigurations:</span></span><br><span class="line">          <span class="string">&#x27;[/**]&#x27;</span><span class="string">:</span></span><br><span class="line">            <span class="attr">allowedOrigins:</span> <span class="string">&quot;*&quot;</span></span><br><span class="line">            <span class="attr">allowedMethods:</span> <span class="string">&quot;*&quot;</span></span><br><span class="line">            <span class="attr">allowedHeaders:</span> <span class="string">&quot;*&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 网关启动类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GatewayApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(GatewayApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-动态路由配置"><a href="#6-3-动态路由配置" class="headerlink" title="6.3 动态路由配置"></a>6.3 动态路由配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">server-addr:</span> <span class="string">localhost:8848</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">locator:</span></span><br><span class="line">          <span class="attr">enabled:</span> <span class="literal">true</span>  <span class="comment"># 启用服务发现路由</span></span><br><span class="line">          <span class="attr">lower-case-service-id:</span> <span class="literal">true</span>  <span class="comment"># 服务 ID 小写</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过 Nacos 配置中心动态配置路由</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@RefreshScope</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DynamicRouteConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RouteDefinitionWriter routeDefinitionWriter;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ApplicationEventPublisher publisher;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 动态添加路由</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addRoute</span><span class="params">(RouteDefinition definition)</span> &#123;</span><br><span class="line">        routeDefinitionWriter.save(Mono.just(definition)).subscribe();</span><br><span class="line">        publisher.publishEvent(<span class="keyword">new</span> <span class="title class_">RefreshRoutesEvent</span>(<span class="built_in">this</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 动态删除路由</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteRoute</span><span class="params">(String routeId)</span> &#123;</span><br><span class="line">        routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();</span><br><span class="line">        publisher.publishEvent(<span class="keyword">new</span> <span class="title class_">RefreshRoutesEvent</span>(<span class="built_in">this</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-4-网关过滤器"><a href="#6-4-网关过滤器" class="headerlink" title="6.4 网关过滤器"></a>6.4 网关过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义网关过滤器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestTimeFilter</span> <span class="keyword">extends</span> <span class="title class_">AbstractGatewayFilterFactory</span>&lt;RequestTimeFilter.Config&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">RequestTimeFilter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(Config.class);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> GatewayFilter <span class="title function_">apply</span><span class="params">(Config config)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (exchange, chain) -&gt; &#123;</span><br><span class="line">            <span class="comment">// 前置逻辑</span></span><br><span class="line">            <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">            System.out.println(<span class="string">&quot;🔔 请求开始：&quot;</span> + exchange.getRequest().getURI());</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> chain.filter(exchange).then(</span><br><span class="line">                Mono.fromRunnable(() -&gt; &#123;</span><br><span class="line">                    <span class="comment">// 后置逻辑</span></span><br><span class="line">                    <span class="type">long</span> <span class="variable">duration</span> <span class="operator">=</span> System.currentTimeMillis() - startTime;</span><br><span class="line">                    System.out.println(<span class="string">&quot;🔕 请求结束，耗时：&quot;</span> + duration + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">                &#125;)</span><br><span class="line">            );</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">        <span class="comment">// 配置属性</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、配置中心：Spring-Cloud-Config"><a href="#七、配置中心：Spring-Cloud-Config" class="headerlink" title="七、配置中心：Spring Cloud Config"></a>七、配置中心：Spring Cloud Config</h2><h3 id="7-1-Config-简介"><a href="#7-1-Config-简介" class="headerlink" title="7.1 Config 简介"></a>7.1 Config 简介</h3><p>Spring Cloud Config 为分布式系统中的<strong>配置文件管理</strong>提供了服务端和客户端支持。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["⚙️ 配置中心架构"] --> B["Config Server"]    B --> C["本地 Git 仓库"]    B --> D["Nacos Config"]    C --> E["Config Client\n（各微服务）"]    D --> E        style A fill:#fff3e0    style B fill:#c8e6c9</pre></div><h3 id="7-2-Config-Server-配置"><a href="#7-2-Config-Server-配置" class="headerlink" title="7.2 Config Server 配置"></a>7.2 Config Server 配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># config-server/application.yml</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8847</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">config-server</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">config:</span></span><br><span class="line">      <span class="attr">server:</span></span><br><span class="line">        <span class="attr">nacos:</span></span><br><span class="line">          <span class="attr">server-addr:</span> <span class="string">localhost:8848</span>  <span class="comment"># 使用 Nacos 作为配置中心</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 配置中心服务端启动类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableConfigServer</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigServerApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ConfigServerApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-3-Nacos-配置中心（推荐）"><a href="#7-3-Nacos-配置中心（推荐）" class="headerlink" title="7.3 Nacos 配置中心（推荐）"></a>7.3 Nacos 配置中心（推荐）</h3><p>Nacos 同时支持<strong>服务注册发现</strong>和<strong>配置管理</strong>，是更加一体化 solution：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 所有微服务共享的配置</span></span><br><span class="line"><span class="comment"># Data ID: shared-config.yaml</span></span><br><span class="line"><span class="comment"># Group: DEFAULT_GROUP</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/demo</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">$&#123;MYSQL_PASSWORD:123456&#125;</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">localhost</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line"></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath:/mapper/**/*.xml</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">com.example.entity</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在各微服务中读取共享配置</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProviderApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(ProviderApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、服务熔断：Sentinel"><a href="#八、服务熔断：Sentinel" class="headerlink" title="八、服务熔断：Sentinel"></a>八、服务熔断：Sentinel</h2><h3 id="8-1-Sentinel-简介"><a href="#8-1-Sentinel-简介" class="headerlink" title="8.1 Sentinel 简介"></a>8.1 Sentinel 简介</h3><p>Sentinel（哨兵）是阿里巴巴开源的<strong>流量控制、熔断降级和系统负载保护</strong>组件。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🛡️ Sentinel 核心功能"] --> B["📊 流量控制"]    A --> C["💥 熔断降级"]    A --> D["🔗 系统自适应"]    A --> E["📈 实时监控"]        B --> B1["QPS 控制\n并发控制"]    C --> C1["响应时间熔断\n异常比例熔断"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#ffcdd2    style D fill:#e3f2fd    style E fill:#fff3e0</pre></div><h3 id="8-2-快速入门"><a href="#8-2-快速入门" class="headerlink" title="8.2 快速入门"></a>8.2 快速入门</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">sentinel:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">      <span class="comment"># Sentinel 控制台地址</span></span><br><span class="line">      <span class="attr">transport:</span></span><br><span class="line">        <span class="attr">dashboard:</span> <span class="string">localhost:8080</span></span><br><span class="line">      <span class="comment"># 配置文件持久化（可选）</span></span><br><span class="line">      <span class="attr">datasource:</span></span><br><span class="line">        <span class="attr">ds:</span></span><br><span class="line">          <span class="attr">nacos:</span></span><br><span class="line">            <span class="attr">server-addr:</span> <span class="string">localhost:8848</span></span><br><span class="line">            <span class="attr">dataId:</span> <span class="string">$&#123;spring.application.name&#125;-sentinel</span></span><br><span class="line">            <span class="attr">groupId:</span> <span class="string">DEFAULT_GROUP</span></span><br><span class="line">            <span class="attr">dataType:</span> <span class="string">json</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用 Sentinel 注解进行熔断降级</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/sentinel&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SentinelController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 定义资源名称</span></span><br><span class="line"><span class="comment">     * fallbackClass 指定降级处理类</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="meta">@SentinelResource(value = &quot;helloResource&quot;, </span></span><br><span class="line"><span class="meta">                      fallback = &quot;helloFallback&quot;,</span></span><br><span class="line"><span class="meta">                      blockHandler = &quot;helloBlockHandler&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(<span class="meta">@RequestParam</span> String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello &quot;</span> + name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 降级处理方法</span></span><br><span class="line"><span class="comment">     * 方法签名必须与原方法一致</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">helloFallback</span><span class="params">(String name, Throwable throwable)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;😢 Sorry, &quot;</span> + name + <span class="string">&quot;, service is unavailable: &quot;</span> + throwable.getMessage();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 流控处理方法</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">helloBlockHandler</span><span class="params">(String name, BlockException ex)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;🚦 Sorry, &quot;</span> + name + <span class="string">&quot;, request is rejected by flow control&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-Feign-集成-Sentinel"><a href="#8-3-Feign-集成-Sentinel" class="headerlink" title="8.3 Feign 集成 Sentinel"></a>8.3 Feign 集成 Sentinel</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启用 Feign Sentinel 降级</span></span><br><span class="line"><span class="attr">feign:</span></span><br><span class="line">  <span class="attr">sentinel:</span></span><br><span class="line">    <span class="attr">enabled:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Feign 客户端配置降级</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@FeignClient(name = &quot;provider-service&quot;, </span></span><br><span class="line"><span class="meta">             fallback = ProviderFeignClientFallback.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ProviderFeignClient</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 降级处理实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProviderFeignClientFallback</span> <span class="keyword">implements</span> <span class="title class_">ProviderFeignClient</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;😢 Service is currently unavailable&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、消息驱动：Spring-Cloud-Stream"><a href="#九、消息驱动：Spring-Cloud-Stream" class="headerlink" title="九、消息驱动：Spring Cloud Stream"></a>九、消息驱动：Spring Cloud Stream</h2><h3 id="9-1-Stream-简介"><a href="#9-1-Stream-简介" class="headerlink" title="9.1 Stream 简介"></a>9.1 Stream 简介</h3><p>Spring Cloud Stream 是消息中间件的抽象层，支持 <strong>RabbitMQ、Kafka、RocketMQ</strong> 等多种消息队列。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📨 Spring Cloud Stream"] --> B["绑定器抽象"]    A --> C["发布-订阅模型"]    A --> D["消费组支持"]        B --> B1["RabbitMQ\nKafka\nRocketMQ"]        style A fill:#fff3e0    style B fill:#c8e6c9</pre></div><h3 id="9-2-快速入门"><a href="#9-2-快速入门" class="headerlink" title="9.2 快速入门"></a>9.2 快速入门</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">stream:</span></span><br><span class="line">      <span class="comment"># 定义绑定器</span></span><br><span class="line">      <span class="attr">binders:</span></span><br><span class="line">        <span class="attr">local-kafka:</span></span><br><span class="line">          <span class="attr">type:</span> <span class="string">kafka</span></span><br><span class="line">          <span class="attr">environment:</span></span><br><span class="line">            <span class="attr">spring.cloud.stream.kafka.binder.brokers:</span> <span class="string">localhost:9092</span></span><br><span class="line">      <span class="comment"># 定义绑定</span></span><br><span class="line">      <span class="attr">bindings:</span></span><br><span class="line">        <span class="attr">output:</span></span><br><span class="line">          <span class="attr">binder:</span> <span class="string">local-kafka</span></span><br><span class="line">          <span class="attr">destination:</span> <span class="string">test-topic</span></span><br><span class="line">        <span class="attr">input:</span></span><br><span class="line">          <span class="attr">binder:</span> <span class="string">local-kafka</span></span><br><span class="line">          <span class="attr">destination:</span> <span class="string">test-topic</span></span><br><span class="line">          <span class="attr">group:</span> <span class="string">consumer-group-1</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 消息生产者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@EnableBinding(Source.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageProducer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> MessageChannel output;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        output.send(MessageBuilder.withPayload(message)</span><br><span class="line">                .setHeader(<span class="string">&quot;content-type&quot;</span>, <span class="string">&quot;text/plain&quot;</span>)</span><br><span class="line">                .build());</span><br><span class="line">        System.out.println(<span class="string">&quot;📤 Sent: &quot;</span> + message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 消息消费者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@EnableBinding(Sink.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@StreamListener(Sink.INPUT)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receiveMessage</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;📥 Received: &quot;</span> + message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十、微服务实战：完整项目构建"><a href="#十、微服务实战：完整项目构建" class="headerlink" title="十、微服务实战：完整项目构建"></a>十、微服务实战：完整项目构建</h2><h3 id="10-1-项目结构设计"><a href="#10-1-项目结构设计" class="headerlink" title="10.1 项目结构设计"></a>10.1 项目结构设计</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📁 microservice-project"] --> B["🛤️ gateway-service"]    A --> C["👤 user-service"]    A --> D["📦 order-service"]    A --> E["⚙️ config-service"]    A --> F["🛡️ sentinel-dashboard"]        B --> B1["端口 8080\n网关路由"]    C --> C1["端口 8081\n用户服务"]    D --> D1["端口 8082\n订单服务"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#c8e6c9</pre></div><h3 id="10-2-服务间调用示例"><a href="#10-2-服务间调用示例" class="headerlink" title="10.2 服务间调用示例"></a>10.2 服务间调用示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * OrderService 调用 UserService</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RestTemplate restTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Order <span class="title function_">createOrder</span><span class="params">(Long userId, String product)</span> &#123;</span><br><span class="line">        <span class="comment">// 调用用户服务获取用户信息</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">userUrl</span> <span class="operator">=</span> <span class="string">&quot;http://user-service/user/&quot;</span> + userId;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> restTemplate.getForObject(userUrl, User.class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建订单</span></span><br><span class="line">        <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Order</span>();</span><br><span class="line">        order.setUserId(userId);</span><br><span class="line">        order.setUserName(user.getName());</span><br><span class="line">        order.setProduct(product);</span><br><span class="line">        order.setCreateTime(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> orderRepository.save(order);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="10-3-分布式事务示例"><a href="#10-3-分布式事务示例" class="headerlink" title="10.3 分布式事务示例"></a>10.3 分布式事务示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Seata 分布式事务配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableAutoDataSourceProxy</span></span><br><span class="line"><span class="meta">@EnableGlobalTransaction</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TransactionConfig</span> &#123;</span><br><span class="line">    <span class="comment">// Seata AT 模式自动配置</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用 <span class="doctag">@GlobalTransactional</span> 注解开启分布式事务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GlobalTransactional(rollbackFor = Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">createOrderWithTransaction</span><span class="params">(Long userId, String product)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 创建订单</span></span><br><span class="line">        <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Order</span>();</span><br><span class="line">        order.setUserId(userId);</span><br><span class="line">        orderRepository.save(order);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 扣减库存（远程调用）</span></span><br><span class="line">        inventoryClient.deduct(product, <span class="number">1</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. 扣减余额（远程调用）</span></span><br><span class="line">        accountClient.deduct(userId, product.getPrice());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 如果任何一步失败，整个事务自动回滚</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十一、微服务安全：Spring-Cloud-Security"><a href="#十一、微服务安全：Spring-Cloud-Security" class="headerlink" title="十一、微服务安全：Spring Cloud Security"></a>十一、微服务安全：Spring Cloud Security</h2><h3 id="11-1-OAuth2-授权模式"><a href="#11-1-OAuth2-授权模式" class="headerlink" title="11.1 OAuth2 授权模式"></a>11.1 OAuth2 授权模式</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔐 OAuth2 授权模式"] --> B["授权码模式\nAuthorization Code"]    A --> C["简化模式\nImplicit"]    A --> D["密码模式\nPassword"]    A --> E["客户端模式\nClient Credentials"]        B --> B1["✅ 最安全\n适合前后端分离"]    C --> C1["⚠️ 不推荐\n适合纯前端应用"]    D --> D1["⚠️ 不推荐\n信任内部系统"]    E --> E1["适合服务间调用"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#fff3e0    style E fill:#e3f2fd</pre></div><h3 id="11-2-JWT-Token-认证"><a href="#11-2-JWT-Token-认证" class="headerlink" title="11.2 JWT Token 认证"></a>11.2 JWT Token 认证</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">security:</span></span><br><span class="line">    <span class="attr">oauth2:</span></span><br><span class="line">      <span class="attr">resourceserver:</span></span><br><span class="line">        <span class="attr">jwt:</span></span><br><span class="line">          <span class="comment"># 使用 Nacos 配置的公钥</span></span><br><span class="line">          <span class="attr">jwk-set-uri:</span> <span class="string">http://localhost:8848/nacos/discovery/v2/jwks</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * OAuth2 资源服务器配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableResourceServer</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResourceServerConfig</span> <span class="keyword">extends</span> <span class="title class_">ResourceServerConfigurerAdapter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">configure</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .authorizeRequests()</span><br><span class="line">            .antMatchers(<span class="string">&quot;/user/**&quot;</span>).hasAuthority(<span class="string">&quot;SCOPE_user:read&quot;</span>)</span><br><span class="line">            .antMatchers(<span class="string">&quot;/order/**&quot;</span>).hasAuthority(<span class="string">&quot;SCOPE_order:write&quot;</span>)</span><br><span class="line">            .anyRequest().authenticated();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取当前用户信息</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/me&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> UserPrincipal <span class="title function_">getCurrentUser</span><span class="params">(<span class="meta">@AuthenticationPrincipal</span> Jwt jwt)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">UserPrincipal</span>(</span><br><span class="line">            jwt.getSubject(),</span><br><span class="line">            jwt.getClaimAsString(<span class="string">&quot;email&quot;</span>),</span><br><span class="line">            jwt.getClaimAsStringList(<span class="string">&quot;roles&quot;</span>)</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十二、总结与展望"><a href="#十二、总结与展望" class="headerlink" title="十二、总结与展望"></a>十二、总结与展望</h2><h3 id="12-1-核心知识点回顾"><a href="#12-1-核心知识点回顾" class="headerlink" title="12.1 核心知识点回顾"></a>12.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Spring Cloud 微服务))    服务治理      Nacos 注册发现      Consul    网关路由      Gateway      动态路由    配置中心      Nacos Config      Spring Cloud Config    服务调用      OpenFeign      RestTemplate    负载均衡      Ribbon      LoadBalancer    熔断降级      Sentinel      Resilience4j    消息驱动      Stream      RabbitMQ      Kafka    安全认证      OAuth2      JWT    分布式事务      Seata    日志追踪      Sleuth      Zipkin</pre></div><h3 id="12-2-学习路线建议"><a href="#12-2-学习路线建议" class="headerlink" title="12.2 学习路线建议"></a>12.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["微服务学习路线"] --> B["第一阶段\n基础入门"]    B --> C["第二阶段\n核心组件"]    C --> D["第三阶段\n服务治理"]    D --> E["第四阶段\n高级特性"]        B --> B1["微服务概念\nSpring Boot"]    C --> C1["Gateway\nNacos\nOpenFeign"]    D --> D1["Sentinel\nConfig\nSecurity"]    E --> E1["分布式事务\n链路追踪"]        style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Spring Cloud 提供了构建微服务架构的完整工具箱，但掌握这些组件只是基础。真正的微服务开发还需要理解分布式系统的复杂性，如CAP理论、一致性、事务等。希望本文能帮助你开启微服务之旅，在实践中不断深化理解！☁️</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/spring-cloud-guide/</id>
    <link href="https://blog.codenav.top/spring-cloud-guide/"/>
    <published>2026-05-24T11:16:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Spring-Cloud-微服务架构详解：从入门到实战-☁️"><a href="#Spring-Cloud-微服务架构详解：从入门到实战-☁️" class="headerlink" title="Spring Cloud 微服务架构详解：从入门到实战 ☁️">]]>
    </summary>
    <title>Spring Cloud 微服务架构详解：从入门到实战 ☁️</title>
    <updated>2026-05-24T11:18:57.595Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="安全" scheme="https://blog.codenav.top/tags/%E5%AE%89%E5%85%A8/"/>
    <category term="认证" scheme="https://blog.codenav.top/tags/%E8%AE%A4%E8%AF%81/"/>
    <category term="授权" scheme="https://blog.codenav.top/tags/%E6%8E%88%E6%9D%83/"/>
    <category term="Shiro" scheme="https://blog.codenav.top/tags/Shiro/"/>
    <content>
      <![CDATA[<h1 id="Apache-Shiro-安全框架详解：从入门到实战-🛡️"><a href="#Apache-Shiro-安全框架详解：从入门到实战-🛡️" class="headerlink" title="Apache Shiro 安全框架详解：从入门到实战 🛡️"></a>Apache Shiro 安全框架详解：从入门到实战 🛡️</h1><blockquote><p>Apache Shiro 是一个强大且易用的 Java 安全框架，提供认证、授权、加密、会话管理等完整的安全功能。相比 Spring Security，Shiro 更加轻量、配置更加简洁，学习曲线更平缓。本文将带你全面理解 Shiro 的核心概念、使用方法以及实战应用！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80shiro-%E6%A6%82%E8%BF%B0%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-shiro">一、Shiro 概述：为什么选择 Shiro？</a></li><li><a href="#%E4%BA%8Cshiro-%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84%E4%B8%8E%E6%A6%82%E5%BF%B5">二、Shiro 核心架构与概念</a></li><li><a href="#%E4%B8%89%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E4%BA%94%E5%88%86%E9%92%9F%E8%B7%91%E9%80%9A-shiro">三、快速入门：五分钟跑通 Shiro</a></li><li><a href="#%E5%9B%9Bshiro-%E8%AE%A4%E8%AF%81%E6%B5%81%E7%A8%8B%E8%AF%A6%E8%A7%A3">四、Shiro 认证流程详解</a></li><li><a href="#%E4%BA%94%E6%8E%88%E6%9D%83%E4%B8%8E%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6">五、授权与权限控制</a></li><li><a href="#%E5%85%ADshiro-%E5%86%85%E7%BD%AE%E8%BF%87%E6%BB%A4%E5%99%A8">六、Shiro 内置过滤器</a></li><li><a href="#%E4%B8%83shiro-%E4%BC%9A%E8%AF%9D%E7%AE%A1%E7%90%86">七、Shiro 会话管理</a></li><li><a href="#%E5%85%ABshiro-%E5%8A%A0%E5%AF%86%E4%B8%8E%E5%AF%86%E7%A0%81%E7%AE%A1%E7%90%86">八、Shiro 加密与密码管理</a></li><li><a href="#%E4%B9%9Dshiro-%E4%B8%8E-spring-boot-%E9%9B%86%E6%88%90">九、Shiro 与 Spring Boot 集成</a></li><li><a href="#%E5%8D%81shiro-%E4%B8%8E-redis-%E9%9B%86%E6%88%90">十、Shiro 与 Redis 集成</a></li><li><a href="#%E5%8D%81%E4%B8%80%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十一、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%BA%8C%E6%80%BB%E7%BB%93">十二、总结</a></li></ul><hr><h2 id="一、Shiro-概述：为什么选择-Shiro？"><a href="#一、Shiro-概述：为什么选择-Shiro？" class="headerlink" title="一、Shiro 概述：为什么选择 Shiro？"></a>一、Shiro 概述：为什么选择 Shiro？</h2><h3 id="1-1-Shiro-简介"><a href="#1-1-Shiro-简介" class="headerlink" title="1.1 Shiro 简介"></a>1.1 Shiro 简介</h3><p>Apache Shiro 是 Apache 软件基金会的顶级项目，前身为 JSecurity（2004年）。它是一个<strong>功能强大且易于使用</strong>的 Java 安全框架，旨在简化应用程序的安全管理工作。</p><p><strong>Shiro 的设计理念：</strong></p><blockquote><p>“Apache Shiro 提供了一套完整的安全管理功能，任何应用都可以轻松获得强大的安全能力，而无需依赖容器特定的配置。”</p></blockquote><h3 id="1-2-Shiro-vs-Spring-Security-对比"><a href="#1-2-Shiro-vs-Spring-Security-对比" class="headerlink" title="1.2 Shiro vs Spring Security 对比"></a>1.2 Shiro vs Spring Security 对比</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🛡️ Java 安全框架对比"] --> B["🔐 Shiro"]    A --> C["🔒 Spring Security"]        B --> B1["✅ 配置简洁\n学习曲线平缓"]    B --> B2["✅ 轻量级\n依赖少"]    B --> B3["✅ 独立使用\n不依赖 Spring"]    B --> B4["⚠️ 功能相对较少\n社区较小"]        C --> C1["✅ 功能完善\nSpring 生态深度集成"]    C --> C2["✅ 社区活跃\n持续更新"]    C --> C3["⚠️ 配置复杂\n学习曲线陡峭"]    C --> C4["⚠️ 强依赖 Spring"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd</pre></div><table><thead><tr><th>对比维度</th><th>Shiro</th><th>Spring Security</th></tr></thead><tbody><tr><td><strong>学习曲线</strong></td><td>平缓，易上手</td><td>陡峭</td></tr><tr><td><strong>配置复杂度</strong></td><td>简单，INI 配置</td><td>复杂，Java 配置</td></tr><tr><td><strong>依赖</strong></td><td>独立，依赖少</td><td>强依赖 Spring</td></tr><tr><td><strong>社区活跃度</strong></td><td>一般</td><td>非常活跃</td></tr><tr><td><strong>文档</strong></td><td>英文为主</td><td>中文资料丰富</td></tr><tr><td><strong>适用场景</strong></td><td>轻量级应用、微服务</td><td>企业级应用、Spring 全家桶</td></tr><tr><td><strong>与 Spring Boot 集成</strong></td><td>shiro-spring-boot-web-starter</td><td>spring-boot-starter-security</td></tr></tbody></table><h3 id="1-3-Shiro-核心功能"><a href="#1-3-Shiro-核心功能" class="headerlink" title="1.3 Shiro 核心功能"></a>1.3 Shiro 核心功能</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🛡️ Shiro 核心功能"] --> B["🕵️ Authentication\n身份认证"]    A --> C["🔑 Authorization\n授权"]    A --> D["🔐 Cryptography\n加密"]    A --> E["📦 Session Management\n会话管理"]    A --> F["🌐 Web Support\nWeb 支持"]    A --> G["🧩 Caching\n缓存支持"]        B --> B1["用户名密码\n手机验证码\n第三方登录"]        C --> C1["角色权限\n资源权限\nRBAC 模型"]        D --> D1["MD5/SHA\nAES/RSA\n密码哈希"]        E --> E1["Session API\n分布式会话\n会话监听"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0    style G fill:#e3f2fd</pre></div><h3 id="1-4-Shiro-特性一览"><a href="#1-4-Shiro-特性一览" class="headerlink" title="1.4 Shiro 特性一览"></a>1.4 Shiro 特性一览</h3><table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td><strong>易用性</strong></td><td>API 简洁直观，上手快</td></tr><tr><td><strong>独立性</strong></td><td>不依赖容器，可独立运行</td></tr><tr><td><strong>灵活性</strong></td><td>可在任意 Java 程序中使用</td></tr><tr><td><strong>Web 支持</strong></td><td>强大的 URL 过滤和拦截</td></tr><tr><td><strong>可扩展</strong></td><td>自定义组件易于替换</td></tr><tr><td><strong>Session</strong></td><td>独立于容器的会话管理</td></tr><tr><td><strong>缓存</strong></td><td>支持 Ehcache、Redis 等缓存</td></tr><tr><td><strong>测试</strong></td><td>支持 JUnit、TestNG 等测试框架</td></tr></tbody></table><hr><h2 id="二、Shiro-核心架构与概念"><a href="#二、Shiro-核心架构与概念" class="headerlink" title="二、Shiro 核心架构与概念"></a>二、Shiro 核心架构与概念</h2><h3 id="2-1-Shiro-架构图"><a href="#2-1-Shiro-架构图" class="headerlink" title="2.1 Shiro 架构图"></a>2.1 Shiro 架构图</h3><p>Shiro 的核心架构由三个主要部分组成：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🧱 Shiro 核心架构"] --> B["📱 Subject\n主体"]    A --> C["🔧 SecurityManager\n安全管理器"]    A --> D["🗄️ Realm\n领域"]        B --> B1["当前操作用户\n可以是人\n也可以是服务"]        C --> C1["Shiro 核心\n管理所有组件"]    C1 --> C2["Authenticator\n认证器"]    C1 --> C3["Authorizer\n授权器"]    C1 --> C4["SessionManager\n会话管理器"]    C1 --> C5["CacheManager\n缓存管理器"]        D --> D1["连接 SecurityManager\n和程序数据\n（用户、角色、权限）"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0</pre></div><h3 id="2-2-核心组件详解"><a href="#2-2-核心组件详解" class="headerlink" title="2.2 核心组件详解"></a>2.2 核心组件详解</h3><p><strong>1️⃣ Subject（主体）</strong></p><p>Subject 是 Shiro 的核心概念之一，代表<strong>当前与程序交互的实体</strong>（用户、第三方服务等）。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取当前登录用户</span></span><br><span class="line"><span class="type">Subject</span> <span class="variable">currentUser</span> <span class="operator">=</span> SecurityUtils.getSubject();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 常用操作</span></span><br><span class="line"><span class="keyword">if</span> (currentUser.isAuthenticated()) &#123;</span><br><span class="line">    <span class="comment">// 用户已认证</span></span><br><span class="line">    currentUser.getPrincipal();  <span class="comment">// 获取身份信息</span></span><br><span class="line">    currentUser.getSession();   <span class="comment">// 获取会话</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>2️⃣ SecurityManager（安全管理器）</strong></p><p>SecurityManager 是 Shiro 的核心管理器，<strong>协调所有安全相关的组件</strong>。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔧 SecurityManager"] --> B["Authenticator\n认证器"]    A --> C["Authorizer\n授权器"]    A --> D["SessionManager\n会话管理器"]    A --> E["CacheManager\n缓存管理器"]        B --> B1["认证策略\n（AtLeastOneSuccessfulStrategy）"]        C --> C1["权限模式\n（AuthorizingRealm）"]        D --> D1["NativeSessionManager\n（原生会话）"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0</pre></div><p><strong>3️⃣ Realm（领域）</strong></p><p>Realm 是连接 Shiro 和程序数据（如数据库）的桥梁。当进行认证和授权时，Shiro 会从 Realm 中获取数据。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义 Realm 示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomRealm</span> <span class="keyword">extends</span> <span class="title class_">AuthorizingRealm</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 认证方法：验证用户身份</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> AuthenticationInfo <span class="title function_">doGetAuthenticationInfo</span><span class="params">(</span></span><br><span class="line"><span class="params">            AuthenticationToken token)</span> <span class="keyword">throws</span> AuthenticationException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">UsernamePasswordToken</span> <span class="variable">upToken</span> <span class="operator">=</span> (UsernamePasswordToken) token;</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> upToken.getUsername();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 从数据库查询用户信息</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userDao.findByUsername(username);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (user == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnknownAccountException</span>(<span class="string">&quot;用户不存在&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 返回认证信息</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SimpleAuthenticationInfo</span>(</span><br><span class="line">            user.getUsername(),  <span class="comment">// 身份（Principal）</span></span><br><span class="line">            user.getPassword(),  <span class="comment">// 凭证（Credential）</span></span><br><span class="line">            getName()            <span class="comment">// Realm 名称</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 授权方法：获取用户权限</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> AuthorizationInfo <span class="title function_">doGetAuthorizationInfo</span><span class="params">(</span></span><br><span class="line"><span class="params">            PrincipalCollection principals)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> (String) principals.getPrimaryPrincipal();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 从数据库查询角色和权限</span></span><br><span class="line">        Set&lt;String&gt; roles = roleDao.findRolesByUsername(username);</span><br><span class="line">        Set&lt;String&gt; permissions = permissionDao.findPermissionsByUsername(username);</span><br><span class="line">        </span><br><span class="line">        <span class="type">SimpleAuthorizationInfo</span> <span class="variable">info</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleAuthorizationInfo</span>();</span><br><span class="line">        info.setRoles(roles);</span><br><span class="line">        info.setStringPermissions(permissions);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> info;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-3-Shiro-认证与授权流程"><a href="#2-3-Shiro-认证与授权流程" class="headerlink" title="2.3 Shiro 认证与授权流程"></a>2.3 Shiro 认证与授权流程</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔐 Shiro 认证授权流程"] --> B["1️⃣ Subject\n提交凭证"]    B --> C["2️⃣ SecurityManager\n接收请求"]    C --> D["3️⃣ Authenticator\n执行认证"]    D --> E["4️⃣ Realm\n查询用户数据"]    E --> F{"认证成功？"}    F -->|"是"| G["5️⃣ 创建\nAuthenticationInfo"]    F -->|"否"| H["6️⃣ 抛出\nAuthenticationException"]    G --> I["7️⃣ Subject\n绑定会话"]    I --> J["8️⃣ 授权检查\n（按需触发）"]    J --> K{"有权限？"}    K -->|"是"| L["✅ 允许访问"]    K -->|"否"| M["❌ 拒绝访问"]        style A fill:#fff3e0    style G fill:#c8e6c9    style H fill:#ffcdd2    style L fill:#c8e6c9    style M fill:#ffcdd2</pre></div><hr><h2 id="三、快速入门：五分钟跑通-Shiro"><a href="#三、快速入门：五分钟跑通-Shiro" class="headerlink" title="三、快速入门：五分钟跑通 Shiro"></a>三、快速入门：五分钟跑通 Shiro</h2><h3 id="3-1-添加-Maven-依赖"><a href="#3-1-添加-Maven-依赖" class="headerlink" title="3.1 添加 Maven 依赖"></a>3.1 添加 Maven 依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Shiro 核心 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Shiro Web 支持 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Shiro Spring Boot 集成 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-spring-boot-web-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Shiro 缓存支持 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-ehcache<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-2-Shiro-INI-配置（经典方式）"><a href="#3-2-Shiro-INI-配置（经典方式）" class="headerlink" title="3.2 Shiro INI 配置（经典方式）"></a>3.2 Shiro INI 配置（经典方式）</h3><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; shiro.ini - Shiro 经典配置文件</span></span><br><span class="line"></span><br><span class="line"><span class="section">[main]</span></span><br><span class="line"><span class="comment">; 定义密码加密器</span></span><br><span class="line"><span class="attr">credentialsMatcher</span>=org.apache.shiro.authc.credential.HashedCredentialsMatcher</span><br><span class="line"><span class="attr">credentialsMatcher.hashAlgorithmName</span>=MD5</span><br><span class="line"><span class="attr">credentialsMatcher.hashIterations</span>=<span class="number">2</span></span><br><span class="line"><span class="attr">credentialsMatcher.storedCredentialsHexEncoded</span>=<span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 定义 Realm</span></span><br><span class="line"><span class="attr">customRealm</span>=com.example.realm.CustomRealm</span><br><span class="line"><span class="attr">customRealm.credentialsMatcher</span>=<span class="variable">$credentialsMatcher</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 定义缓存管理器</span></span><br><span class="line"><span class="attr">cacheManager</span>=org.apache.shiro.cache.MemoryConstrainedCacheManager</span><br><span class="line"><span class="attr">customRealm.cacheManager</span>=<span class="variable">$cacheManager</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 配置 SecurityManager</span></span><br><span class="line"><span class="attr">securityManager.realms</span>=<span class="variable">$customRealm</span></span><br><span class="line"><span class="attr">securityManager.cacheManager</span>=<span class="variable">$cacheManager</span></span><br><span class="line"></span><br><span class="line"><span class="section">[users]</span></span><br><span class="line"><span class="comment">; 用户名=密码,角色列表</span></span><br><span class="line"><span class="attr">admin</span>=admin123,admin,user</span><br><span class="line"><span class="attr">test</span>=test123,user</span><br><span class="line"></span><br><span class="line"><span class="section">[roles]</span></span><br><span class="line"><span class="comment">; 角色=权限列表</span></span><br><span class="line"><span class="attr">admin</span>=*</span><br><span class="line"><span class="attr">user</span>=article:read,article:write</span><br><span class="line"></span><br><span class="line"><span class="section">[urls]</span></span><br><span class="line"><span class="comment">; URL=过滤器链</span></span><br><span class="line">/<span class="attr">login</span>=anon</span><br><span class="line">/static/**=anon</span><br><span class="line">/<span class="attr">logout</span>=logout</span><br><span class="line">/admin/**=authc,roles<span class="section">[admin]</span></span><br><span class="line">/user/**=authc</span><br><span class="line">/**=authc</span><br></pre></td></tr></table></figure><h3 id="3-3-Shiro-Hello-World"><a href="#3-3-Shiro-Hello-World" class="headerlink" title="3.3 Shiro Hello World"></a>3.3 Shiro Hello World</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shiro Hello World 示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShiroHelloWorld</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 加载 INI 配置</span></span><br><span class="line">        Factory&lt;SecurityManager&gt; factory = <span class="keyword">new</span> <span class="title class_">IniSecurityManagerFactory</span>(<span class="string">&quot;classpath:shiro.ini&quot;</span>);</span><br><span class="line">        <span class="type">SecurityManager</span> <span class="variable">securityManager</span> <span class="operator">=</span> factory.getInstance();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 设置 SecurityManager（全局）</span></span><br><span class="line">        SecurityUtils.setSecurityManager(securityManager);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. 获取当前用户</span></span><br><span class="line">        <span class="type">Subject</span> <span class="variable">currentUser</span> <span class="operator">=</span> SecurityUtils.getSubject();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 4. 执行登录</span></span><br><span class="line">        <span class="type">UsernamePasswordToken</span> <span class="variable">token</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UsernamePasswordToken</span>(<span class="string">&quot;admin&quot;</span>, <span class="string">&quot;admin123&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            currentUser.login(token);</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 登录成功！&quot;</span>);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 5. 检查角色</span></span><br><span class="line">            <span class="keyword">if</span> (currentUser.hasRole(<span class="string">&quot;admin&quot;</span>)) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;👤 用户是管理员&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 6. 检查权限</span></span><br><span class="line">            <span class="keyword">if</span> (currentUser.isPermitted(<span class="string">&quot;article:write&quot;</span>)) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;✍️ 用户可以写文章&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (UnknownAccountException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 用户不存在&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IncorrectCredentialsException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 密码错误&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (AuthenticationException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 认证失败：&quot;</span> + e.getMessage());</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 7. 登出</span></span><br><span class="line">            currentUser.logout();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、Shiro-认证流程详解"><a href="#四、Shiro-认证流程详解" class="headerlink" title="四、Shiro 认证流程详解"></a>四、Shiro 认证流程详解</h2><h3 id="4-1-AuthenticationToken-详解"><a href="#4-1-AuthenticationToken-详解" class="headerlink" title="4.1 AuthenticationToken 详解"></a>4.1 AuthenticationToken 详解</h3><p>AuthenticationToken 是用户身份凭证的抽象，Shiro 使用它来携带认证信息：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 最常用的 UsernamePasswordToken</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">UsernamePasswordToken</span> <span class="variable">token</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UsernamePasswordToken</span>(</span><br><span class="line">    <span class="string">&quot;username&quot;</span>,           <span class="comment">// 用户名</span></span><br><span class="line">    <span class="string">&quot;password&quot;</span>            <span class="comment">// 密码</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可选参数</span></span><br><span class="line">token.setRememberMe(<span class="literal">true</span>);  <span class="comment">// 记住我</span></span><br><span class="line">token.setHost(<span class="string">&quot;192.168.1.1&quot;</span>);  <span class="comment">// 登录主机地址</span></span><br></pre></td></tr></table></figure><h3 id="4-2-AuthenticationInfo-详解"><a href="#4-2-AuthenticationInfo-详解" class="headerlink" title="4.2 AuthenticationInfo 详解"></a>4.2 AuthenticationInfo 详解</h3><p>AuthenticationInfo 是认证信息的抽象，包含三个核心元素：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📋 AuthenticationInfo"] --> B["Principal\n身份标识"]    A --> C["Credential\n凭证"]    A --> D["Realm Name\nRealm 名称"]        B --> B1["用户名\n用户 ID\n用户对象"]        C --> C1["密码\n证书"]        D --> D1["来自哪个\nRealm 的认证"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * SimpleAuthenticationInfo 示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomRealm</span> <span class="keyword">extends</span> <span class="title class_">AuthorizingRealm</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> AuthenticationInfo <span class="title function_">doGetAuthenticationInfo</span><span class="params">(</span></span><br><span class="line"><span class="params">            AuthenticationToken token)</span> <span class="keyword">throws</span> AuthenticationException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">UsernamePasswordToken</span> <span class="variable">loginToken</span> <span class="operator">=</span> (UsernamePasswordToken) token;</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> loginToken.getUsername();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 查询用户</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userDao.findByUsername(username);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (user == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnknownAccountException</span>(<span class="string">&quot;用户不存在&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 返回 SimpleAuthenticationInfo</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SimpleAuthenticationInfo</span>(</span><br><span class="line">            user.getUsername(),   <span class="comment">// Principal：身份标识（通常是用户名）</span></span><br><span class="line">            user.getPassword(),    <span class="comment">// Credential：凭证（通常是加密后的密码）</span></span><br><span class="line">            ByteSource.Util.bytes(user.getSalt()),  <span class="comment">//盐值，用于密码比对</span></span><br><span class="line">            getName()             <span class="comment">// Realm 名称</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-3-密码加密与比对"><a href="#4-3-密码加密与比对" class="headerlink" title="4.3 密码加密与比对"></a>4.3 密码加密与比对</h3><p>Shiro 支持多种密码加密算法，推荐使用 <strong>Hash + Salt</strong> 方式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 配置密码加密器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> CredentialsMatcher <span class="title function_">credentialsMatcher</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 配置 HashedCredentialsMatcher</span></span><br><span class="line">    <span class="type">HashedCredentialsMatcher</span> <span class="variable">matcher</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashedCredentialsMatcher</span>();</span><br><span class="line">    matcher.setHashAlgorithmName(<span class="string">&quot;SHA-256&quot;</span>);  <span class="comment">// 加密算法</span></span><br><span class="line">    matcher.setHashIterations(<span class="number">1024</span>);          <span class="comment">// 迭代次数</span></span><br><span class="line">    matcher.setStoredCredentialsHexEncoded(<span class="literal">false</span>);  <span class="comment">// Hex 编码</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> matcher;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> CustomRealm <span class="title function_">customRealm</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">CustomRealm</span> <span class="variable">realm</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CustomRealm</span>();</span><br><span class="line">    realm.setCredentialsMatcher(credentialsMatcher());</span><br><span class="line">    <span class="keyword">return</span> realm;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 密码加密示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PasswordUtil</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 生成密码哈希</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">encryptPassword</span><span class="params">(String rawPassword, String salt)</span> &#123;</span><br><span class="line">        <span class="type">SimpleHash</span> <span class="variable">hash</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleHash</span>(</span><br><span class="line">            <span class="string">&quot;SHA-256&quot;</span>,      <span class="comment">// 算法</span></span><br><span class="line">            rawPassword,    <span class="comment">// 明文密码</span></span><br><span class="line">            salt,           <span class="comment">// 盐值</span></span><br><span class="line">            <span class="number">1024</span>           <span class="comment">// 迭代次数</span></span><br><span class="line">        );</span><br><span class="line">        <span class="keyword">return</span> hash.toHex();  <span class="comment">// 返回十六进制字符串</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 生成随机盐值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">generateSalt</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">byte</span>[] salt = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">16</span>];</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">SecureRandom</span>().nextBytes(salt);</span><br><span class="line">        <span class="keyword">return</span> Base64.getEncoder().encodeToString(salt);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-4-多种认证方式"><a href="#4-4-多种认证方式" class="headerlink" title="4.4 多种认证方式"></a>4.4 多种认证方式</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多种认证方式实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 用户名密码认证（最常用）</span></span><br><span class="line"><span class="type">UsernamePasswordToken</span> <span class="variable">token</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UsernamePasswordToken</span>(username, password);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 表单认证（Web 应用）</span></span><br><span class="line"><span class="type">UsernamePasswordToken</span> <span class="variable">token</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UsernamePasswordToken</span>(username, password, rememberMe);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. JWT Token 认证（自定义 Token）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtToken</span> <span class="keyword">implements</span> <span class="title class_">AuthenticationToken</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String token;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">JwtToken</span><span class="params">(String token)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.token = token;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getPrincipal</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> token;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getCredentials</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> token;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 自定义认证（手机号验证码）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SmsToken</span> <span class="keyword">implements</span> <span class="title class_">AuthenticationToken</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String phoneNumber;</span><br><span class="line">    <span class="keyword">private</span> String smsCode;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SmsToken</span><span class="params">(String phoneNumber, String smsCode)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.phoneNumber = phoneNumber;</span><br><span class="line">        <span class="built_in">this</span>.smsCode = smsCode;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getPrincipal</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> phoneNumber;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getCredentials</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> smsCode;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、授权与权限控制"><a href="#五、授权与权限控制" class="headerlink" title="五、授权与权限控制"></a>五、授权与权限控制</h2><h3 id="5-1-授权的三种方式"><a href="#5-1-授权的三种方式" class="headerlink" title="5.1 授权的三种方式"></a>5.1 授权的三种方式</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔑 授权方式"] --> B["1️⃣ 编程式授权"]    A --> C["2️⃣ 注解式授权"]    A --> D["3️⃣ 标签式授权"]        B --> B1["在代码中\n手动判断"]        C --> C1["使用注解\n@RequiresRoles\n@RequiresPermissions"]        D --> D1["在页面模板中\n使用 Shiro 标签"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd</pre></div><p><strong>1️⃣ 编程式授权</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Subject</span> <span class="variable">currentUser</span> <span class="operator">=</span> SecurityUtils.getSubject();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查角色</span></span><br><span class="line"><span class="keyword">if</span> (currentUser.hasRole(<span class="string">&quot;admin&quot;</span>)) &#123;</span><br><span class="line">    <span class="comment">// 有管理员角色</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查多个角色（需要同时具备）</span></span><br><span class="line"><span class="keyword">if</span> (currentUser.hasAllRoles(Arrays.asList(<span class="string">&quot;admin&quot;</span>, <span class="string">&quot;user&quot;</span>))) &#123;</span><br><span class="line">    <span class="comment">// 同时具备 admin 和 user 角色</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查权限</span></span><br><span class="line"><span class="keyword">if</span> (currentUser.isPermitted(<span class="string">&quot;article:write&quot;</span>)) &#123;</span><br><span class="line">    <span class="comment">// 有写文章权限</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查多个权限（只需具备其一）</span></span><br><span class="line"><span class="keyword">if</span> (currentUser.isPermitted(<span class="string">&quot;article:write&quot;</span>, <span class="string">&quot;article:edit&quot;</span>)) &#123;</span><br><span class="line">    <span class="comment">// 有写或编辑文章权限</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 批量检查权限</span></span><br><span class="line"><span class="keyword">if</span> (currentUser.isPermitted(<span class="string">&quot;article:*&quot;</span>)) &#123;</span><br><span class="line">    <span class="comment">// 有文章相关的所有权限</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>2️⃣ 注解式授权</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用注解进行授权控制</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要 ADMIN 角色</span></span><br><span class="line">    <span class="meta">@RequiresRoles(&quot;admin&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUser</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        userRepository.deleteById(userId);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要多个角色之一</span></span><br><span class="line">    <span class="meta">@RequiresRoles(value = &#123;&quot;admin&quot;, &quot;manager&quot;&#125;, logical = Logical.OR)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exportData</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 导出数据</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要特定权限</span></span><br><span class="line">    <span class="meta">@RequiresPermissions(&quot;user:create&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">createUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.save(user);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要多个权限</span></span><br><span class="line">    <span class="meta">@RequiresPermissions(value = &#123;&quot;user:update&quot;, &quot;user:read&quot;&#125;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">updateUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.update(user);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 允许任何人访问</span></span><br><span class="line">    <span class="meta">@RequiresGuest</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">register</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="comment">// 注册逻辑</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要已认证用户</span></span><br><span class="line">    <span class="meta">@RequiresAuthentication</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getProfile</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 获取用户资料</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>3️⃣ JSP 标签式授权</strong></p><figure class="highlight jsp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 在 JSP 页面中使用 Shiro 标签 --&gt;</span><br><span class="line">&lt;%@ taglib prefix=<span class="string">&quot;shiro&quot;</span> uri=<span class="string">&quot;http://shiro.apache.org/tags&quot;</span> %&gt;</span><br><span class="line"></span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 显示已认证用户 --&gt;</span><br><span class="line">&lt;shiro:authenticated&gt;</span><br><span class="line">    欢迎，&lt;shiro:principal/&gt;！</span><br><span class="line">    &lt;a href=<span class="string">&quot;/logout&quot;</span>&gt;退出&lt;/a&gt;</span><br><span class="line">&lt;/shiro:authenticated&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 显示未认证用户 --&gt;</span><br><span class="line">&lt;shiro:notAuthenticated&gt;</span><br><span class="line">    &lt;a href=<span class="string">&quot;/login&quot;</span>&gt;登录&lt;/a&gt;</span><br><span class="line">&lt;/shiro:notAuthenticated&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 角色检查 --&gt;</span><br><span class="line">&lt;shiro:hasRole name=<span class="string">&quot;admin&quot;</span>&gt;</span><br><span class="line">    &lt;a href=<span class="string">&quot;/admin&quot;</span>&gt;管理后台&lt;/a&gt;</span><br><span class="line">&lt;/shiro:hasRole&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 权限检查 --&gt;</span><br><span class="line">&lt;shiro:hasPermission name=<span class="string">&quot;article:write&quot;</span>&gt;</span><br><span class="line">    &lt;a href=<span class="string">&quot;/article/write&quot;</span>&gt;写文章&lt;/a&gt;</span><br><span class="line">&lt;/shiro:hasPermission&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 多个角色检查 --&gt;</span><br><span class="line">&lt;shiro:hasAnyRoles name=<span class="string">&quot;admin,manager&quot;</span>&gt;</span><br><span class="line">    您是管理员或经理</span><br><span class="line">&lt;/shiro:hasAnyRoles&gt;</span><br><span class="line"></span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><h3 id="5-2-Shiro-内置注解说明"><a href="#5-2-Shiro-内置注解说明" class="headerlink" title="5.2 Shiro 内置注解说明"></a>5.2 Shiro 内置注解说明</h3><table><thead><tr><th>注解</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>@RequiresAuthentication</strong></td><td>需要已认证用户</td><td><code>@RequiresAuthentication</code></td></tr><tr><td><strong>@RequiresUser</strong></td><td>需要已认证或记住我用户</td><td><code>@RequiresUser</code></td></tr><tr><td><strong>@RequiresGuest</strong></td><td>需要未认证的访客</td><td><code>@RequiresGuest</code></td></tr><tr><td><strong>@RequiresRoles</strong></td><td>需要特定角色</td><td><code>@RequiresRoles(&quot;admin&quot;)</code></td></tr><tr><td><strong>@RequiresPermissions</strong></td><td>需要特定权限</td><td><code>@RequiresPermissions(&quot;user:create&quot;)</code></td></tr><tr><td><strong>@RequiresServer</strong></td><td>需要特定服务器</td><td><code>@RequiresServer</code></td></tr></tbody></table><h3 id="5-3-权限的通配符规则"><a href="#5-3-权限的通配符规则" class="headerlink" title="5.3 权限的通配符规则"></a>5.3 权限的通配符规则</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shiro 权限通配符规则</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 精确权限</span></span><br><span class="line"><span class="string">&quot;user:create&quot;</span>           <span class="comment">// 创建用户权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 多值权限</span></span><br><span class="line"><span class="string">&quot;user:create,read,update&quot;</span>  <span class="comment">// 多个权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 所有操作</span></span><br><span class="line"><span class="string">&quot;user:*&quot;</span>                <span class="comment">// 用户相关的所有权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 所有用户</span></span><br><span class="line"><span class="string">&quot;*:*&quot;</span>                   <span class="comment">// 所有权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 系统级别权限</span></span><br><span class="line"><span class="string">&quot;system:user:manage&quot;</span>   <span class="comment">// 系统用户管理</span></span><br></pre></td></tr></table></figure><hr><h2 id="六、Shiro-内置过滤器"><a href="#六、Shiro-内置过滤器" class="headerlink" title="六、Shiro 内置过滤器"></a>六、Shiro 内置过滤器</h2><h3 id="6-1-过滤器配置"><a href="#6-1-过滤器配置" class="headerlink" title="6.1 过滤器配置"></a>6.1 过滤器配置</h3><p>Shiro 通过过滤器链来拦截请求并进行安全控制：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[urls]</span></span><br><span class="line"><span class="comment">; 格式：URL = 过滤器链</span></span><br><span class="line">/<span class="attr">login</span> = anon                    <span class="comment">; 匿名访问</span></span><br><span class="line">/<span class="attr">logout</span> = logout                <span class="comment">; 登出</span></span><br><span class="line">/static/** = anon                <span class="comment">; 静态资源匿名访问</span></span><br><span class="line">/admin/** = authc, roles<span class="section">[admin]</span>  <span class="comment">; 需要认证且有 admin 角色</span></span><br><span class="line">/user/** = authc                 <span class="comment">; 需要认证</span></span><br><span class="line">/** = anon                       <span class="comment">; 其他全部匿名访问</span></span><br></pre></td></tr></table></figure><h3 id="6-2-常用过滤器一览"><a href="#6-2-常用过滤器一览" class="headerlink" title="6.2 常用过滤器一览"></a>6.2 常用过滤器一览</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔗 Shiro 内置过滤器"] --> B["anon\n匿名访问"]    A --> C["authc\n需要认证"]    A --> D["authcBasic\nHTTP Basic"]    A --> E["roles\n角色检查"]    A --> F["perms\n权限检查"]    A --> G["ssl\nHTTPS"]        B --> B1["无需认证\n可自由访问"]        C --> C1["必须成功认证\n否则跳转登录"]        D --> D1["需要 HTTP\nBasic 认证"]        E --> E1["必须拥有\n指定角色"]        F --> F1["必须拥有\n指定权限"]        G --> G1["必须使用\nHTTPS 协议"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0    style G fill:#e3f2fd</pre></div><h3 id="6-3-过滤器配置示例"><a href="#6-3-过滤器配置示例" class="headerlink" title="6.3 过滤器配置示例"></a>6.3 过滤器配置示例</h3><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[urls]</span></span><br><span class="line"><span class="comment">; 1. 匿名过滤器 - 任何人都可以访问</span></span><br><span class="line">/<span class="attr">login</span> = anon</span><br><span class="line">/<span class="attr">register</span> = anon</span><br><span class="line">/<span class="attr">captcha</span> = anon</span><br><span class="line"></span><br><span class="line"><span class="comment">; 2. 认证过滤器 - 必须登录才能访问</span></span><br><span class="line">/<span class="attr">home</span> = authc</span><br><span class="line">/<span class="attr">profile</span> = authc</span><br><span class="line"></span><br><span class="line"><span class="comment">; 3. 角色过滤器 - 必须拥有特定角色</span></span><br><span class="line">/admin/** = authc, roles<span class="section">[admin]</span></span><br><span class="line">/manager/** = authc, roles<span class="section">[admin, manager]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 4. 权限过滤器 - 必须拥有特定权限</span></span><br><span class="line">/user/<span class="attr">create</span> = authc, perms[user:create]</span><br><span class="line">/user/<span class="attr">delete</span> = authc, perms[user:delete]</span><br><span class="line"></span><br><span class="line"><span class="comment">; 5. HTTP Basic 认证</span></span><br><span class="line">/api/** = authcBasic</span><br><span class="line"></span><br><span class="line"><span class="comment">; 6. SSL 强制跳转</span></span><br><span class="line">/payment/** = ssl<span class="section">[443]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">; 7. 组合过滤器</span></span><br><span class="line">/<span class="attr">dashboard</span> = authc, roles[user], perms[dashboard:read]</span><br></pre></td></tr></table></figure><h3 id="6-4-自定义过滤器"><a href="#6-4-自定义过滤器" class="headerlink" title="6.4 自定义过滤器"></a>6.4 自定义过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义过滤器示例：IP 白名单过滤器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Ip</span> whitelistFilter <span class="keyword">extends</span> <span class="title class_">AccessControlFilter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Set&lt;String&gt; allowedIPs = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAllowedIPs</span><span class="params">(String ips)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.allowedIPs = Arrays.asList(ips.split(<span class="string">&quot;,&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">boolean</span> <span class="title function_">isAccessAllowed</span><span class="params">(ServletRequest request, </span></span><br><span class="line"><span class="params">                                     ServletResponse response, </span></span><br><span class="line"><span class="params">                                     Object mappedValue)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">clientIp</span> <span class="operator">=</span> getClientIp(request);</span><br><span class="line">        <span class="keyword">return</span> allowedIPs.contains(clientIp);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">boolean</span> <span class="title function_">onAccessDenied</span><span class="params">(ServletRequest request, </span></span><br><span class="line"><span class="params">                                      ServletResponse response)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">HttpServletResponse</span> <span class="variable">httpResponse</span> <span class="operator">=</span> (HttpServletResponse) response;</span><br><span class="line">        httpResponse.setStatus(<span class="number">403</span>);</span><br><span class="line">        httpResponse.getWriter().write(<span class="string">&quot;&#123;\&quot;error\&quot;:\&quot;IP not allowed\&quot;&#125;&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">getClientIp</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">ip</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;X-Forwarded-For&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (ip == <span class="literal">null</span> || ip.isEmpty() || <span class="string">&quot;unknown&quot;</span>.equalsIgnoreCase(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;X-Real-IP&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (ip == <span class="literal">null</span> || ip.isEmpty() || <span class="string">&quot;unknown&quot;</span>.equalsIgnoreCase(ip)) &#123;</span><br><span class="line">            ip = request.getRemoteAddr();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ip;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、Shiro-会话管理"><a href="#七、Shiro-会话管理" class="headerlink" title="七、Shiro 会话管理"></a>七、Shiro 会话管理</h2><h3 id="7-1-Shiro-会话概述"><a href="#7-1-Shiro-会话概述" class="headerlink" title="7.1 Shiro 会话概述"></a>7.1 Shiro 会话概述</h3><p>Shiro 提供了一套<strong>独立于容器</strong>的会话管理框架，具有以下特点：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📦 Shiro 会话特性"] --> B["🧬 容器无关性"]    A --> C["🕐 可配置超时"]    A --> D["🌐 Web 支持"]    A --> E["📊 会话监听"]    A --> F["💾 会话存储"]        B --> B1["不依赖 Servlet 容器\n可在普通 Java 程序中使用"]        C --> C1["可设置会话\n超时时间"]        D --> D1["提供 Web SessionFilter\n自动管理 Web 会话"]        E --> E1["监听会话创建\n销毁等事件"]        F --> F1["支持内存、Redis\nEhcache 等存储"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="7-2-Session-API-使用"><a href="#7-2-Session-API-使用" class="headerlink" title="7.2 Session API 使用"></a>7.2 Session API 使用</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shiro Session 使用示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SessionDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sessionDemo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Subject</span> <span class="variable">currentUser</span> <span class="operator">=</span> SecurityUtils.getSubject();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取会话（如果不存在则创建）</span></span><br><span class="line">        <span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> currentUser.getSession();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置会话属性</span></span><br><span class="line">        session.setAttribute(<span class="string">&quot;userId&quot;</span>, <span class="number">1001L</span>);</span><br><span class="line">        session.setAttribute(<span class="string">&quot;username&quot;</span>, <span class="string">&quot;admin&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取会话属性</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> (Long) session.getAttribute(<span class="string">&quot;userId&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取会话信息</span></span><br><span class="line">        session.getId();           <span class="comment">// 会话 ID</span></span><br><span class="line">        session.getStartTimestamp(); <span class="comment">// 创建时间</span></span><br><span class="line">        session.getLastAccessTime(); <span class="comment">// 最后访问时间</span></span><br><span class="line">        session.getTimeout();       <span class="comment">// 超时时间</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置超时时间（毫秒）</span></span><br><span class="line">        session.setTimeout(<span class="number">1800000</span>);  <span class="comment">// 30 分钟</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 使会话失效</span></span><br><span class="line">        session.stop();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-3-会话监听器"><a href="#7-3-会话监听器" class="headerlink" title="7.3 会话监听器"></a>7.3 会话监听器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义会话监听器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomSessionListener</span> <span class="keyword">implements</span> <span class="title class_">SessionListener</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LoggerFactory.getLogger(getClass());</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onStart</span><span class="params">(Session session)</span> &#123;</span><br><span class="line">        logger.info(<span class="string">&quot;会话创建：&#123;&#125;&quot;</span>, session.getId());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onStop</span><span class="params">(Session session)</span> &#123;</span><br><span class="line">        logger.info(<span class="string">&quot;会话停止：&#123;&#125;&quot;</span>, session.getId());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onExpiration</span><span class="params">(Session session)</span> &#123;</span><br><span class="line">        logger.info(<span class="string">&quot;会话过期：&#123;&#125;&quot;</span>, session.getId());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 配置会话监听器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> SessionManager <span class="title function_">sessionManager</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">DefaultWebSessionManager</span> <span class="variable">sessionManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSessionManager</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 设置会话超时</span></span><br><span class="line">    sessionManager.setGlobalSessionTimeout(<span class="number">1800000</span>);  <span class="comment">// 30 分钟</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 配置监听器</span></span><br><span class="line">    List&lt;SessionListener&gt; listeners = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    listeners.add(<span class="keyword">new</span> <span class="title class_">CustomSessionListener</span>());</span><br><span class="line">    sessionManager.setSessionListeners(listeners);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> sessionManager;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、Shiro-加密与密码管理"><a href="#八、Shiro-加密与密码管理" class="headerlink" title="八、Shiro 加密与密码管理"></a>八、Shiro 加密与密码管理</h2><h3 id="8-1-Shiro-加密支持"><a href="#8-1-Shiro-加密支持" class="headerlink" title="8.1 Shiro 加密支持"></a>8.1 Shiro 加密支持</h3><p>Shiro 提供了完整的加密支持，包括对称加密、非对称加密和哈希加密：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔐 Shiro 加密支持"] --> B["🧊 Hash 加密"]    A --> C["🔑 AES/RSA"]    A --> D["📝 Base64"]        B --> B1["MD5/SHA\n带盐迭代"]        C --> C1["对称/非对称\n加密解密"]        D --> D1["编码解码\n数据传输"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd</pre></div><h3 id="8-2-Hash-加密详解"><a href="#8-2-Hash-加密详解" class="headerlink" title="8.2 Hash 加密详解"></a>8.2 Hash 加密详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Hash 加密工具类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HashUtil</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * MD5 加密（简单）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">md5</span><span class="params">(String input)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SimpleHash</span>(<span class="string">&quot;MD5&quot;</span>, input).toHex();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * SHA-256 加密（推荐）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">sha256</span><span class="params">(String input)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SimpleHash</span>(<span class="string">&quot;SHA-256&quot;</span>, input).toHex();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 带盐 SHA-256 加密</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">sha256WithSalt</span><span class="params">(String input, String salt)</span> &#123;</span><br><span class="line">        <span class="type">SimpleHash</span> <span class="variable">hash</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleHash</span>(</span><br><span class="line">            <span class="string">&quot;SHA-256&quot;</span>,</span><br><span class="line">            input,</span><br><span class="line">            salt,</span><br><span class="line">            <span class="number">1024</span>  <span class="comment">// 迭代次数</span></span><br><span class="line">        );</span><br><span class="line">        <span class="keyword">return</span> hash.toHex();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 生成随机盐值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">generateSalt</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">byte</span>[] salt = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">16</span>];</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">SecureRandom</span>().nextBytes(salt);</span><br><span class="line">        <span class="keyword">return</span> Base64.getEncoder().encodeToString(salt);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 验证密码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">verifyPassword</span><span class="params">(String rawPassword, </span></span><br><span class="line"><span class="params">                                        String storedPasswordHash, </span></span><br><span class="line"><span class="params">                                        String salt)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">calculatedHash</span> <span class="operator">=</span> sha256WithSalt(rawPassword, salt);</span><br><span class="line">        <span class="keyword">return</span> calculatedHash.equals(storedPasswordHash);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-Shiro-内置加密工具类"><a href="#8-3-Shiro-内置加密工具类" class="headerlink" title="8.3 Shiro 内置加密工具类"></a>8.3 Shiro 内置加密工具类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shiro Codec 模块提供的加密工具</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShiroCodecDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. Base64 编码解码</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">original</span> <span class="operator">=</span> <span class="string">&quot;Hello, Shiro! 🛡️&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">encoded</span> <span class="operator">=</span> Base64.encodeToString(original.getBytes());</span><br><span class="line">        <span class="type">byte</span>[] decoded = Base64.decode(encoded);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. Hex 编码解码</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">hex</span> <span class="operator">=</span> Hex.encodeToString(original.getBytes());</span><br><span class="line">        <span class="type">byte</span>[] fromHex = Hex.decode(hex);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. AesCipherService（AES 加密）</span></span><br><span class="line">        <span class="type">AesCipherService</span> <span class="variable">aes</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesCipherService</span>();</span><br><span class="line">        aes.setKeySize(<span class="number">128</span>);  <span class="comment">// 128/256 位</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 加密</span></span><br><span class="line">        <span class="type">byte</span>[] encrypted = aes.encrypt(</span><br><span class="line">            original.getBytes(), </span><br><span class="line">            key.getBytes()</span><br><span class="line">        ).getBytes();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 解密</span></span><br><span class="line">        <span class="type">byte</span>[] decrypted = aes.decrypt(encrypted, key.getBytes()).getBytes();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 4. DefaultPasswordService（密码服务）</span></span><br><span class="line">        <span class="type">DefaultPasswordService</span> <span class="variable">passwordService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultPasswordService</span>();</span><br><span class="line">        <span class="type">String</span> <span class="variable">hashedPassword</span> <span class="operator">=</span> passwordService.encryptPassword(<span class="string">&quot;password123&quot;</span>);</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">matches</span> <span class="operator">=</span> passwordService.passwordsMatch(<span class="string">&quot;password123&quot;</span>, hashedPassword);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、Shiro-与-Spring-Boot-集成"><a href="#九、Shiro-与-Spring-Boot-集成" class="headerlink" title="九、Shiro 与 Spring Boot 集成"></a>九、Shiro 与 Spring Boot 集成</h2><h3 id="9-1-Maven-依赖"><a href="#9-1-Maven-依赖" class="headerlink" title="9.1 Maven 依赖"></a>9.1 Maven 依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Shiro Spring Boot 集成 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-spring-boot-web-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Shiro Session Redis 存储（可选） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="9-2-Shiro-配置类"><a href="#9-2-Shiro-配置类" class="headerlink" title="9.2 Shiro 配置类"></a>9.2 Shiro 配置类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shiro 配置类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShiroConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置 SecurityManager</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityManager <span class="title function_">securityManager</span><span class="params">(</span></span><br><span class="line"><span class="params">            CustomRealm customRealm,</span></span><br><span class="line"><span class="params">            SessionManager sessionManager,</span></span><br><span class="line"><span class="params">            CacheManager cacheManager)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">DefaultWebSecurityManager</span> <span class="variable">manager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSecurityManager</span>();</span><br><span class="line">        manager.setRealm(customRealm);</span><br><span class="line">        manager.setSessionManager(sessionManager);</span><br><span class="line">        manager.setCacheManager(cacheManager);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> manager;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置 ShiroFilterChainDefinition</span></span><br><span class="line"><span class="comment">     * 定义 URL 与过滤器的映射关系</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> ShiroFilterChainDefinition <span class="title function_">shiroFilterChainDefinition</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultShiroFilterChainDefinition</span> <span class="variable">chainDefinition</span> <span class="operator">=</span> </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">DefaultShiroFilterChainDefinition</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 公开路径</span></span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/login&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/register&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/static/**&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 需要认证的路径</span></span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/user/**&quot;</span>, <span class="string">&quot;authc&quot;</span>);</span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/admin/**&quot;</span>, <span class="string">&quot;authc,roles[admin]&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 登出</span></span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/logout&quot;</span>, <span class="string">&quot;logout&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 其他全部需要认证</span></span><br><span class="line">        chainDefinition.addPathDefinition(<span class="string">&quot;/**&quot;</span>, <span class="string">&quot;authc&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> chainDefinition;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 启用 Shiro 注解</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> AuthorizationAttributeSourceAdvisor <span class="title function_">authorizationAttributeSourceAdvisor</span><span class="params">(</span></span><br><span class="line"><span class="params">            SecurityManager securityManager)</span> &#123;</span><br><span class="line">        <span class="type">AuthorizationAttributeSourceAdvisor</span> <span class="variable">advisor</span> <span class="operator">=</span> </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">AuthorizationAttributeSourceAdvisor</span>();</span><br><span class="line">        advisor.setSecurityManager(securityManager);</span><br><span class="line">        <span class="keyword">return</span> advisor;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 启用 Spring AOP 支持</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> DefaultAdvisorAutoProxyCreator <span class="title function_">defaultAdvisorAutoProxyCreator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultAdvisorAutoProxyCreator</span> <span class="variable">proxyCreator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultAdvisorAutoProxyCreator</span>();</span><br><span class="line">        proxyCreator.setProxyTargetClass(<span class="literal">true</span>);</span><br><span class="line">        <span class="keyword">return</span> proxyCreator;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-3-完整配置示例"><a href="#9-3-完整配置示例" class="headerlink" title="9.3 完整配置示例"></a>9.3 完整配置示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 完整的 Shiro + Spring Boot 配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(ShiroProperties.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShiroConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ShiroProperties shiroProperties;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> CustomRealm <span class="title function_">customRealm</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">CustomRealm</span> <span class="variable">realm</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CustomRealm</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 配置密码加密器</span></span><br><span class="line">        <span class="type">HashedCredentialsMatcher</span> <span class="variable">matcher</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashedCredentialsMatcher</span>();</span><br><span class="line">        matcher.setHashAlgorithmName(<span class="string">&quot;SHA-256&quot;</span>);</span><br><span class="line">        matcher.setHashIterations(shiroProperties.getHashIterations());</span><br><span class="line">        matcher.setStoredCredentialsHexEncoded(<span class="literal">false</span>);</span><br><span class="line">        realm.setCredentialsMatcher(matcher);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 配置缓存</span></span><br><span class="line">        realm.setCacheManager(shiroRedisCacheManager());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> realm;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityManager <span class="title function_">securityManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultWebSecurityManager</span> <span class="variable">manager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSecurityManager</span>();</span><br><span class="line">        manager.setRealm(customRealm());</span><br><span class="line">        manager.setSessionManager(sessionManager());</span><br><span class="line">        manager.setCacheManager(shiroRedisCacheManager());</span><br><span class="line">        <span class="keyword">return</span> manager;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SessionManager <span class="title function_">sessionManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultWebSessionManager</span> <span class="variable">manager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSessionManager</span>();</span><br><span class="line">        manager.setGlobalSessionTimeout(shiroProperties.getSessionTimeout());</span><br><span class="line">        manager.setSessionIdCookieEnabled(<span class="literal">true</span>);</span><br><span class="line">        manager.setSessionIdUrlRewritingEnabled(<span class="literal">false</span>);</span><br><span class="line">        <span class="keyword">return</span> manager;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> ShiroFilterChainDefinition <span class="title function_">shiroFilterChainDefinition</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultShiroFilterChainDefinition</span> <span class="variable">definition</span> <span class="operator">=</span> </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">DefaultShiroFilterChainDefinition</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 公开路径</span></span><br><span class="line">        definition.addPathDefinition(<span class="string">&quot;/api/auth/**&quot;</span>, <span class="string">&quot;anon&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 需要认证</span></span><br><span class="line">        definition.addPathDefinition(<span class="string">&quot;/api/user/**&quot;</span>, <span class="string">&quot;authc&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 需要管理员角色</span></span><br><span class="line">        definition.addPathDefinition(<span class="string">&quot;/api/admin/**&quot;</span>, <span class="string">&quot;authc,roles[admin]&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> definition;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十、Shiro-与-Redis-集成"><a href="#十、Shiro-与-Redis-集成" class="headerlink" title="十、Shiro 与 Redis 集成"></a>十、Shiro 与 Redis 集成</h2><h3 id="10-1-为什么要使用-Redis-存储会话？"><a href="#10-1-为什么要使用-Redis-存储会话？" class="headerlink" title="10.1 为什么要使用 Redis 存储会话？"></a>10.1 为什么要使用 Redis 存储会话？</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["❌ 单机会话问题"] --> A1["无法跨服务器\n共享会话"]    A1 --> A2["服务器重启\n会话丢失"]        B["✅ Redis 存储优势"] --> B1["跨服务器\n共享会话"]    B1 --> B2["服务器重启\n会话不丢失"]    B1 --> B3["支持分布式\n集群部署"]        style A fill:#ffcdd2    style B fill:#c8e6c9</pre></div><h3 id="10-2-Shiro-Redis-配置"><a href="#10-2-Shiro-Redis-配置" class="headerlink" title="10.2 Shiro Redis 配置"></a>10.2 Shiro Redis 配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shiro Redis 配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShiroRedisConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisConnectionFactory redisConnectionFactory;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置 Redis 管理器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RedisManager <span class="title function_">shiroRedisManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">RedisManager</span> <span class="variable">redisManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RedisManager</span>();</span><br><span class="line">        redisManager.setConnectionFactory(redisConnectionFactory);</span><br><span class="line">        redisManager.setExpire(<span class="number">1800</span>);  <span class="comment">// 30 分钟过期</span></span><br><span class="line">        redisManager.setTimeout(<span class="number">5000</span>);  <span class="comment">// 5 秒超时</span></span><br><span class="line">        redisManager.setKeyPrefix(<span class="string">&quot;shiro:session:&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> redisManager;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置 Session DAO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RedisSessionDAO <span class="title function_">redisSessionDAO</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">RedisSessionDAO</span> <span class="variable">sessionDAO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RedisSessionDAO</span>();</span><br><span class="line">        sessionDAO.setRedisManager(shiroRedisManager());</span><br><span class="line">        <span class="keyword">return</span> sessionDAO;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置缓存 DAO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RedisCacheManager <span class="title function_">shiroRedisCacheManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">RedisCacheManager</span> <span class="variable">cacheManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RedisCacheManager</span>();</span><br><span class="line">        cacheManager.setRedisManager(shiroRedisManager());</span><br><span class="line">        cacheManager.setKeyPrefix(<span class="string">&quot;shiro:cache:&quot;</span>);</span><br><span class="line">        cacheManager.setExpire(<span class="number">3600</span>);  <span class="comment">// 1 小时过期</span></span><br><span class="line">        <span class="keyword">return</span> cacheManager;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置 Session Manager 使用 Redis</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SessionManager <span class="title function_">sessionManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">DefaultWebSessionManager</span> <span class="variable">manager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultWebSessionManager</span>();</span><br><span class="line">        manager.setSessionDAO(redisSessionDAO());</span><br><span class="line">        manager.setGlobalSessionTimeout(<span class="number">1800000</span>);  <span class="comment">// 30 分钟</span></span><br><span class="line">        <span class="keyword">return</span> manager;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十一、常见问题与最佳实践"><a href="#十一、常见问题与最佳实践" class="headerlink" title="十一、常见问题与最佳实践"></a>十一、常见问题与最佳实践</h2><h3 id="11-1-常见问题与解决方案"><a href="#11-1-常见问题与解决方案" class="headerlink" title="11.1 常见问题与解决方案"></a>11.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["⚠️ Subject 不为 null\n但未认证"]    A --> C["⚠️ 过滤器链不生效"]    A --> D["⚠️ 会话 ID 变化"]    A --> E["⚠️ Redis 会话\n无法共享"]        B --> B1["SecurityManager\n未正确配置"]    B1 --> B2["检查 Realm 配置\n是否注入到 SecurityManager"]        C --> C1["URL 匹配规则\n顺序问题"]    C1 --> C2["确保路径配置\n从上到下匹配"]        D --> D1["会话固定攻击防护"]    D1 --> D2["启用会话固定防护"]        E --> E1["Redis Key 冲突"]    E1 --> E2["检查 Key 前缀\n是否一致"]        style A fill:#fff3e0</pre></div><h3 id="11-2-Shiro-配置清单"><a href="#11-2-Shiro-配置清单" class="headerlink" title="11.2 Shiro 配置清单"></a>11.2 Shiro 配置清单</h3><table><thead><tr><th>配置项</th><th>推荐设置</th><th>说明</th></tr></thead><tbody><tr><td><strong>密码加密</strong></td><td>SHA-256 + 盐 + 多次迭代</td><td>安全可靠</td></tr><tr><td><strong>会话超时</strong></td><td>1800 秒（30 分钟）</td><td>平衡安全与体验</td></tr><tr><td><strong>记住我</strong></td><td>可选，最长 7 天</td><td>需评估风险</td></tr><tr><td><strong>CSRF</strong></td><td>启用</td><td>防止跨站请求</td></tr><tr><td><strong>Session Redis</strong></td><td>推荐生产环境使用</td><td>支持分布式</td></tr></tbody></table><h3 id="11-3-安全建议"><a href="#11-3-安全建议" class="headerlink" title="11.3 安全建议"></a>11.3 安全建议</h3><table><thead><tr><th>安全建议</th><th>说明</th></tr></thead><tbody><tr><td><strong>使用 HTTPS</strong></td><td>生产环境必须启用</td></tr><tr><td><strong>密码加密</strong></td><td>避免 MD5 单一加密</td></tr><tr><td><strong>会话管理</strong></td><td>生产环境使用 Redis 存储</td></tr><tr><td><strong>权限最小化</strong></td><td>只授予必要的权限</td></tr><tr><td><strong>日志审计</strong></td><td>记录登录和敏感操作</td></tr></tbody></table><hr><h2 id="十二、总结"><a href="#十二、总结" class="headerlink" title="十二、总结"></a>十二、总结</h2><h3 id="12-1-核心知识点回顾"><a href="#12-1-核心知识点回顾" class="headerlink" title="12.1 核心知识点回顾"></a>12.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Apache Shiro))    核心概念      Subject 主体      SecurityManager 安全管理器      Realm 领域    认证      AuthenticationToken      AuthenticationInfo      密码加密    授权      编程式授权      注解式授权      标签式授权    过滤器      内置过滤器      自定义过滤器      过滤器链配置    会话管理      Session API      SessionListener      Redis 存储    Spring Boot      集成配置      Redis 会话      注解启用</pre></div><h3 id="12-2-学习路线建议"><a href="#12-2-学习路线建议" class="headerlink" title="12.2 学习路线建议"></a>12.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["Shiro 学习路线"] --> B["第一阶段\n基础概念"]    B --> C["第二阶段\n认证授权"]    C --> D["第三阶段\n过滤器"]    D --> E["第四阶段\n会话管理"]    E --> F["第五阶段\nSpring Boot"]        B --> B1["架构组件\n核心接口"]        C --> C1["Realm 实现\n权限控制"]        D --> D1["内置过滤器\n自定义过滤器"]        E --> E1["Session API\nRedis 集成"]        F --> F1["配置集成\n项目实战"]        style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0    style F fill:#e3f2fd</pre></div><h3 id="12-3-Shiro-与-Spring-Security-选择"><a href="#12-3-Shiro-与-Spring-Security-选择" class="headerlink" title="12.3 Shiro 与 Spring Security 选择"></a>12.3 Shiro 与 Spring Security 选择</h3><table><thead><tr><th>场景</th><th>推荐</th></tr></thead><tbody><tr><td><strong>Spring Boot 全家桶项目</strong></td><td>Spring Security</td></tr><tr><td><strong>轻量级独立项目</strong></td><td>Shiro</td></tr><tr><td><strong>快速开发上线</strong></td><td>Shiro</td></tr><tr><td><strong>复杂的企业级安全需求</strong></td><td>Spring Security</td></tr><tr><td><strong>微服务架构</strong></td><td>Spring Security</td></tr><tr><td><strong>非 Spring 项目</strong></td><td>Shiro</td></tr></tbody></table><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Shiro 是一个简洁而不简单的安全框架，它的设计理念是”让安全变得简单”。相比 Spring Security，Shiro 的配置更加直观，非常适合快速上手。希望本文能帮助你快速掌握 Shiro，在项目中实现安全保护！🛡️</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/shiro-framework-guide/</id>
    <link href="https://blog.codenav.top/shiro-framework-guide/"/>
    <published>2026-05-24T11:10:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Apache-Shiro-安全框架详解：从入门到实战-🛡️"><a href="#Apache-Shiro-安全框架详解：从入门到实战-🛡️" class="headerlink" title="Apache Shiro 安全框架详解：从入门到实战 🛡️">]]>
    </summary>
    <title>Apache Shiro 安全框架详解：从入门到实战 🛡️</title>
    <updated>2026-05-24T11:12:07.275Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="Spring Security" scheme="https://blog.codenav.top/tags/Spring-Security/"/>
    <category term="安全" scheme="https://blog.codenav.top/tags/%E5%AE%89%E5%85%A8/"/>
    <category term="认证" scheme="https://blog.codenav.top/tags/%E8%AE%A4%E8%AF%81/"/>
    <category term="授权" scheme="https://blog.codenav.top/tags/%E6%8E%88%E6%9D%83/"/>
    <category term="OAuth2" scheme="https://blog.codenav.top/tags/OAuth2/"/>
    <category term="JWT" scheme="https://blog.codenav.top/tags/JWT/"/>
    <content>
      <![CDATA[<h1 id="Spring-Security-安全框架详解：从入门到实战-🔐"><a href="#Spring-Security-安全框架详解：从入门到实战-🔐" class="headerlink" title="Spring Security 安全框架详解：从入门到实战 🔐"></a>Spring Security 安全框架详解：从入门到实战 🔐</h1><blockquote><p>Spring Security 是 Spring 生态中用于处理<strong>认证（Authentication）和授权（Authorization）</strong>的安全框架，它强大、灵活、可扩展，几乎是所有 Java Web 应用的标配安全解决方案。本文将带你全面理解 Spring Security 的核心概念、工作原理以及实战应用！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80spring-security-%E6%A6%82%E8%BF%B0%E4%BB%80%E4%B9%88%E6%98%AF%E5%AE%89%E5%85%A8%E6%A1%86%E6%9E%B6">一、Spring Security 概述：什么是安全框架？</a></li><li><a href="#%E4%BA%8C%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E8%AE%A4%E8%AF%81%E4%B8%8E%E6%8E%88%E6%9D%83">二、核心概念：认证与授权</a></li><li><a href="#%E4%B8%89%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E4%BA%94%E5%88%86%E9%92%9F%E8%B7%91%E9%80%9A-spring-security">三、快速入门：五分钟跑通 Spring Security</a></li><li><a href="#%E5%9B%9Bspring-security-%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B">四、Spring Security 架构与工作流程</a></li><li><a href="#%E4%BA%94%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81%E8%AF%A6%E8%A7%A3">五、用户认证详解</a></li><li><a href="#%E5%85%AD%E6%8E%88%E6%9D%83%E4%B8%8E%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6">六、授权与权限控制</a></li><li><a href="#%E4%B8%83%E8%BF%87%E6%BB%A4%E5%99%A8%E9%93%BE%E8%AF%A6%E8%A7%A3">七、过滤器链详解</a></li><li><a href="#%E5%85%ABsecurity-%E5%AE%9E%E6%88%98jwt-%E8%AE%A4%E8%AF%81">八、Security 实战：JWT 认证</a></li><li><a href="#%E4%B9%9Doauth2-%E7%AC%AC%E4%B8%89%E6%96%B9%E7%99%BB%E5%BD%95">九、OAuth2 第三方登录</a></li><li><a href="#%E5%8D%81spring-security-%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7">十、Spring Security 高级特性</a></li><li><a href="#%E5%8D%81%E4%B8%80%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十一、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%BA%8C%E6%80%BB%E7%BB%93">十二、总结</a></li></ul><hr><h2 id="一、Spring-Security-概述：什么是安全框架？"><a href="#一、Spring-Security-概述：什么是安全框架？" class="headerlink" title="一、Spring Security 概述：什么是安全框架？"></a>一、Spring Security 概述：什么是安全框架？</h2><h3 id="1-1-为什么需要安全框架？"><a href="#1-1-为什么需要安全框架？" class="headerlink" title="1.1 为什么需要安全框架？"></a>1.1 为什么需要安全框架？</h3><p>在 Web 应用中，我们经常会遇到以下安全问题：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["⚠️ Web 应用常见安全问题"] --> B["🔓 未授权访问"]    A --> C["🔐 密码泄露"]    A --> D["🚫 CSRF 攻击"]    A --> E["🐛 SQL 注入"]    A --> F["🕵️ XSS 攻击"]        B --> B1["访问未授权\n的功能或数据"]    C --> C1["用户密码\n被窃取"]    D --> D1["跨站请求\n伪造攻击"]    E --> E1["恶意 SQL\n注入攻击"]    F --> F1["恶意脚本\n注入攻击"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#ffcdd2    style D fill:#fff3e0    style E fill:#ffcdd2    style F fill:#fff3e0</pre></div><p><strong>常见的 Web 安全威胁：</strong></p><ol><li><strong>未授权访问</strong>：用户没有登录或没有权限，却能访问某些功能</li><li><strong>密码泄露</strong>：用户密码在传输或存储过程中被窃取</li><li><strong>CSRF 攻击</strong>：攻击者诱导用户执行非本意的操作</li><li><strong>SQL 注入</strong>：通过输入框注入恶意 SQL 语句</li><li><strong>XSS 攻击</strong>：在页面中注入恶意脚本</li></ol><h3 id="1-2-Spring-Security-是什么？"><a href="#1-2-Spring-Security-是什么？" class="headerlink" title="1.2 Spring Security 是什么？"></a>1.2 Spring Security 是什么？</h3><p>Spring Security 是一个专注于<strong>为 Java 应用提供认证和授权</strong>的安全框架。它的主要功能包括：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔐 Spring Security 核心功能"] --> B["🕵️ 身份认证"]    A --> C["🔑 授权管理"]    A --> D["🛡️ CSRF 防护"]    A --> E["🔒 会话管理"]    A --> F["📊 密码加密"]    A --> G["🌐 第三方登录"]        B --> B1["用户名密码认证\nOAuth2 认证\nJWT 认证\nLDAP 认证"]        C --> C1["基于角色权限\n基于资源权限\n方法级别权限"]        D --> D1["Token 防护\n表单防护"]        E --> E1["会话固定防护\n并发控制"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0    style G fill:#e3f2fd</pre></div><h3 id="1-3-Spring-Security-的优势"><a href="#1-3-Spring-Security-的优势" class="headerlink" title="1.3 Spring Security 的优势"></a>1.3 Spring Security 的优势</h3><table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td><strong>功能完善</strong></td><td>提供认证、授权、会话管理等完整安全解决方案</td></tr><tr><td><strong>易于集成</strong></td><td>与 Spring Boot、Spring MVC 无缝集成</td></tr><tr><td><strong>高度可扩展</strong></td><td>支持自定义认证方式、各种第三方登录</td></tr><tr><td><strong>社区活跃</strong></td><td>作为 Spring 家族成员，拥有强大的社区支持</td></tr><tr><td><strong>持续更新</strong></td><td>跟随 Spring 生态持续迭代，安全性高</td></tr></tbody></table><h3 id="1-4-Spring-Security-版本选择"><a href="#1-4-Spring-Security-版本选择" class="headerlink" title="1.4 Spring Security 版本选择"></a>1.4 Spring Security 版本选择</h3><table><thead><tr><th>版本</th><th>Spring Boot 版本</th><th>Java 版本要求</th><th>主要特性</th></tr></thead><tbody><tr><td><strong>Spring Security 5.x</strong></td><td>Spring Boot 2.x</td><td>Java 8+</td><td>OAuth2 集成、JWT 支持</td></tr><tr><td><strong>Spring Security 6.x</strong></td><td>Spring Boot 3.x</td><td>Java 17+</td><td>响应式安全、简化 API</td></tr></tbody></table><blockquote><p>💡 <strong>推荐</strong>：新项目建议使用 Spring Boot 3.x + Spring Security 6.x，享受最新特性和性能优化。</p></blockquote><hr><h2 id="二、核心概念：认证与授权"><a href="#二、核心概念：认证与授权" class="headerlink" title="二、核心概念：认证与授权"></a>二、核心概念：认证与授权</h2><h3 id="2-1-认证（Authentication）"><a href="#2-1-认证（Authentication）" class="headerlink" title="2.1 认证（Authentication）"></a>2.1 认证（Authentication）</h3><p><strong>认证</strong>是验证用户身份的过程，通俗来说就是”确认你是谁”。</p><p>常见的认证方式：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🕵️ 认证方式"] --> B["🔐 用户名密码"]    A --> C["📱 手机验证码"]    A --> D["🔑 数字证书"]    A --> E["🌐 OAuth2"]    A --> F["👆 指纹/面部识别"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0    style E fill:#f8bbd0    style F fill:#e3f2fd</pre></div><h3 id="2-2-授权（Authorization）"><a href="#2-2-授权（Authorization）" class="headerlink" title="2.2 授权（Authorization）"></a>2.2 授权（Authorization）</h3><p><strong>授权</strong>是在认证之后，根据用户的身份决定”你能做什么”。</p><p>常见的授权模型：</p><table><thead><tr><th>模型</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>RBAC</strong></td><td>基于角色的访问控制</td><td>用户 -&gt; 角色 -&gt; 权限</td></tr><tr><td><strong>ABAC</strong></td><td>基于属性的访问控制</td><td>根据用户属性动态判断</td></tr><tr><td><strong>DAC</strong></td><td>自主访问控制</td><td>资源所有者自行分配权限</td></tr></tbody></table><h3 id="2-3-认证与授权的关系"><a href="#2-3-认证与授权的关系" class="headerlink" title="2.3 认证与授权的关系"></a>2.3 认证与授权的关系</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["👤 用户"] -->|"1️⃣ 认证"| B["🕵️ 身份验证"]    B -->|"验证成功"| C["🆔 身份标识"]    C -->|"2️⃣ 授权"| D["🔑 权限判断"]    D -->|"有权限"| E["✅ 访问资源"]    D -->|"无权限"| F["❌ 拒绝访问"]        style B fill:#c8e6c9    style D fill:#e3f2fd</pre></div><p><strong>典型流程：</strong></p><ol><li>用户登录系统 → 系统验证用户名密码 → 认证成功，发放 Token</li><li>用户访问某个功能 → 系统检查用户角色 → 判断是否有权限</li><li>有权限 → 允许访问；无权限 → 拒绝访问</li></ol><hr><h2 id="三、快速入门：五分钟跑通-Spring-Security"><a href="#三、快速入门：五分钟跑通-Spring-Security" class="headerlink" title="三、快速入门：五分钟跑通 Spring Security"></a>三、快速入门：五分钟跑通 Spring Security</h2><h3 id="3-1-添加依赖"><a href="#3-1-添加依赖" class="headerlink" title="3.1 添加依赖"></a>3.1 添加依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Spring Boot 3.x --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-security<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Spring Boot 2.x --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-security<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.x<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-2-最简配置：开箱即用"><a href="#3-2-最简配置：开箱即用" class="headerlink" title="3.2 最简配置：开箱即用"></a>3.2 最简配置：开箱即用</h3><p>Spring Boot 环境下，只需添加依赖，无需任何配置，框架会<strong>自动开启安全保护</strong>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Boot 3.x 启动类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Application</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(Application.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>添加依赖后，Spring Security 会自动配置：</p><ul><li>🔒 所有端点需要认证</li><li>🔐 默认生成随机密码（可在控制台看到）</li><li>🔄 启用 CSRF 防护</li><li>🔐 默认启用会话管理</li></ul><h3 id="3-3-自定义安全配置"><a href="#3-3-自定义安全配置" class="headerlink" title="3.3 自定义安全配置"></a>3.3 自定义安全配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 安全配置类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span>  <span class="comment">// 启用 Web 安全</span></span><br><span class="line"><span class="meta">@EnableMethodSecurity</span>  <span class="comment">// 启用方法级别安全（如 @PreAuthorize）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置 HTTP 安全策略</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            <span class="comment">// 禁用 CSRF（前后端分离项目通常需要禁用）</span></span><br><span class="line">            .csrf(csrf -&gt; csrf.disable())</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置授权规则</span></span><br><span class="line">            .authorizeHttpRequests(auth -&gt; auth</span><br><span class="line">                <span class="comment">// 公开静态资源</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/static/**&quot;</span>, <span class="string">&quot;/public/**&quot;</span>).permitAll()</span><br><span class="line">                <span class="comment">// 公开登录页面</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/login&quot;</span>, <span class="string">&quot;/register&quot;</span>).permitAll()</span><br><span class="line">                <span class="comment">// admin 角色专属</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/admin/**&quot;</span>).hasRole(<span class="string">&quot;ADMIN&quot;</span>)</span><br><span class="line">                <span class="comment">// 其他请求需要认证</span></span><br><span class="line">                .anyRequest().authenticated()</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置表单登录</span></span><br><span class="line">            .formLogin(form -&gt; form</span><br><span class="line">                .loginPage(<span class="string">&quot;/login&quot;</span>)           <span class="comment">// 登录页面</span></span><br><span class="line">                .defaultSuccessUrl(<span class="string">&quot;/home&quot;</span>)    <span class="comment">// 登录成功跳转</span></span><br><span class="line">                .failureUrl(<span class="string">&quot;/login?error&quot;</span>)     <span class="comment">// 登录失败跳转</span></span><br><span class="line">                .permitAll()</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置 HTTP Basic 认证</span></span><br><span class="line">            .httpBasic(basic -&gt; basic</span><br><span class="line">                .realmName(<span class="string">&quot;My App&quot;</span>)</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置登出</span></span><br><span class="line">            .logout(logout -&gt; logout</span><br><span class="line">                .logoutUrl(<span class="string">&quot;/logout&quot;</span>)</span><br><span class="line">                .logoutSuccessUrl(<span class="string">&quot;/login?logout&quot;</span>)</span><br><span class="line">                .invalidateHttpSession(<span class="literal">true</span>)</span><br><span class="line">                .deleteCookies(<span class="string">&quot;JSESSIONID&quot;</span>)</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置会话管理</span></span><br><span class="line">            .sessionManagement(session -&gt; session</span><br><span class="line">                .sessionFixation().changeSessionId()  <span class="comment">// 防止会话固定攻击</span></span><br><span class="line">                .maximumSessions(<span class="number">1</span>)                     <span class="comment">// 同一用户最大会话数</span></span><br><span class="line">                .maxSessionsPreventsLogin(<span class="literal">false</span>)      <span class="comment">// 超过后阻止登录</span></span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置密码编码器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> PasswordEncoder <span class="title function_">passwordEncoder</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// BCrypt 是一种安全的单向加密算法，自动加盐</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">BCryptPasswordEncoder</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-自定义用户服务"><a href="#3-4-自定义用户服务" class="headerlink" title="3.4 自定义用户服务"></a>3.4 自定义用户服务</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 实现 UserDetailsService 接口</span></span><br><span class="line"><span class="comment"> * Spring Security 通过这个接口获取用户信息进行认证</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomUserDetailsService</span> <span class="keyword">implements</span> <span class="title class_">UserDetailsService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> PasswordEncoder passwordEncoder;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> UserDetails <span class="title function_">loadUserByUsername</span><span class="params">(String username)</span> </span><br><span class="line">            <span class="keyword">throws</span> UsernameNotFoundException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 1. 根据用户名查询用户</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userRepository.findByUsername(username);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (user == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果用户不存在，抛出异常</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UsernameNotFoundException</span>(<span class="string">&quot;用户不存在：&quot;</span> + username);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 将用户信息转换为 Spring Security 的 UserDetails</span></span><br><span class="line">        List&lt;SimpleGrantedAuthority&gt; authorities = user.getRoles().stream()</span><br><span class="line">            .map(role -&gt; <span class="keyword">new</span> <span class="title class_">SimpleGrantedAuthority</span>(<span class="string">&quot;ROLE_&quot;</span> + role.getName()))</span><br><span class="line">            .collect(Collectors.toList());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">org</span>.springframework.security.core.userdetails.User(</span><br><span class="line">            user.getUsername(),</span><br><span class="line">            user.getPassword(),</span><br><span class="line">            user.getStatus() == <span class="number">1</span>,          <span class="comment">// 账户是否启用</span></span><br><span class="line">            <span class="literal">true</span>,                            <span class="comment">// 账户是否未过期</span></span><br><span class="line">            <span class="literal">true</span>,                            <span class="comment">// 凭证是否未过期</span></span><br><span class="line">            <span class="literal">true</span>,                            <span class="comment">// 账户是否未锁定</span></span><br><span class="line">            authorities                      <span class="comment">// 权限列表</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、Spring-Security-架构与工作流程"><a href="#四、Spring-Security-架构与工作流程" class="headerlink" title="四、Spring Security 架构与工作流程"></a>四、Spring Security 架构与工作流程</h2><h3 id="4-1-核心组件架构"><a href="#4-1-核心组件架构" class="headerlink" title="4.1 核心组件架构"></a>4.1 核心组件架构</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔐 Spring Security 架构"] --> B["🏠 SecurityContextHolder"]    A --> C["🎭 Authentication"]    A --> D["🔓 SecurityFilterChain"]    A --> E["🛡️ AccessDecisionManager"]        B --> B1["存储当前用户\n认证信息"]        C --> C1["Principal\nCredentials\nAuthorities"]        D --> D1["UsernamePassword\nAuthenticationFilter"]    D --> D2["...\n其他过滤器"]        E --> E1["投票器\n决策管理器"]        style A fill:#fff3e0</pre></div><h3 id="4-2-认证流程详解"><a href="#4-2-认证流程详解" class="headerlink" title="4.2 认证流程详解"></a>4.2 认证流程详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔐 Spring Security 认证流程"] --> B["1️⃣ 用户提交\n用户名密码"]    B --> C["2️⃣ Filter 拦截\n请求"]    C --> D["3️⃣ AuthenticationManager\n管理认证"]    D --> E["4️⃣ AuthenticationProvider\n执行认证"]    E --> F["5️⃣ UserDetailsService\n加载用户信息"]    F --> G["6️⃣ PasswordEncoder\n校验密码"]    G --> H{"认证结果？"}    H -->|"成功"| I["7️⃣ SecurityContext\n保存认证信息"]    H -->|"失败"| J["8️⃣ 返回\n认证失败"]    I --> K["9️⃣ AuthenticationSuccessHandler\n处理成功"]    J --> K2["🔙 AuthenticationFailureHandler\n处理失败"]        style A fill:#fff3e0    style I fill:#c8e6c9    style J fill:#ffcdd2</pre></div><p><strong>流程说明：</strong></p><table><thead><tr><th>步骤</th><th>组件</th><th>说明</th></tr></thead><tbody><tr><td><strong>1</strong></td><td>用户提交</td><td>用户在登录页面输入用户名密码</td></tr><tr><td><strong>2</strong></td><td>Filter 拦截</td><td><code>UsernamePasswordAuthenticationFilter</code> 拦截请求</td></tr><tr><td><strong>3</strong></td><td>AuthenticationManager</td><td>认证管理器，委托 AuthenticationProvider</td></tr><tr><td><strong>4</strong></td><td>AuthenticationProvider</td><td>执行具体的认证逻辑</td></tr><tr><td><strong>5</strong></td><td>UserDetailsService</td><td>根据用户名加载用户信息</td></tr><tr><td><strong>6</strong></td><td>PasswordEncoder</td><td>校验密码是否匹配</td></tr><tr><td><strong>7</strong></td><td>SecurityContext</td><td>认证成功后，保存认证信息到上下文</td></tr><tr><td><strong>8-9</strong></td><td>成功&#x2F;失败处理</td><td>执行相应的处理器</td></tr></tbody></table><h3 id="4-3-核心接口与类"><a href="#4-3-核心接口与类" class="headerlink" title="4.3 核心接口与类"></a>4.3 核心接口与类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Security 核心接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. UserDetailsService：加载用户信息</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserDetailsService</span> &#123;</span><br><span class="line">    UserDetails <span class="title function_">loadUserByUsername</span><span class="params">(String username)</span> <span class="keyword">throws</span> UsernameNotFoundException;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. UserDetails：用户信息接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserDetails</span> <span class="keyword">extends</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">    Collection&lt;? <span class="keyword">extends</span> <span class="title class_">GrantedAuthority</span>&gt; getAuthorities();  <span class="comment">// 权限列表</span></span><br><span class="line">    String <span class="title function_">getPassword</span><span class="params">()</span>;        <span class="comment">// 密码</span></span><br><span class="line">    String <span class="title function_">getUsername</span><span class="params">()</span>;        <span class="comment">// 用户名</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isAccountNonExpired</span><span class="params">()</span>;      <span class="comment">// 账户是否未过期</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isAccountNonLocked</span><span class="params">()</span>;       <span class="comment">// 账户是否未锁定</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isCredentialsNonExpired</span><span class="params">()</span>; <span class="comment">// 凭证是否未过期</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isEnabled</span><span class="params">()</span>;               <span class="comment">// 账户是否启用</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. Authentication：认证信息接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Authentication</span> <span class="keyword">extends</span> <span class="title class_">Principal</span> &#123;</span><br><span class="line">    Collection&lt;? <span class="keyword">extends</span> <span class="title class_">GrantedAuthority</span>&gt; getAuthorities();  <span class="comment">// 权限</span></span><br><span class="line">    Object <span class="title function_">getCredentials</span><span class="params">()</span>;      <span class="comment">// 凭证（密码）</span></span><br><span class="line">    Object <span class="title function_">getDetails</span><span class="params">()</span>;          <span class="comment">// 详情</span></span><br><span class="line">    Object <span class="title function_">getPrincipal</span><span class="params">()</span>;        <span class="comment">// 主体（用户信息）</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isAuthenticated</span><span class="params">()</span>;    <span class="comment">// 是否已认证</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setAuthenticated</span><span class="params">(<span class="type">boolean</span> isAuthenticated)</span> <span class="keyword">throws</span> IllegalArgumentException;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. GrantedAuthority：权限接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">GrantedAuthority</span> <span class="keyword">extends</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">    String <span class="title function_">getAuthority</span><span class="params">()</span>;  <span class="comment">// 权限字符串，如 &quot;ROLE_ADMIN&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、用户认证详解"><a href="#五、用户认证详解" class="headerlink" title="五、用户认证详解"></a>五、用户认证详解</h2><h3 id="5-1-认证流程中的核心组件"><a href="#5-1-认证流程中的核心组件" class="headerlink" title="5.1 认证流程中的核心组件"></a>5.1 认证流程中的核心组件</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🕵️ 认证核心组件"] --> B["📋 Authentication\n认证凭证"]    A --> C["🗂️ UserDetails\n用户信息"]    A --> D["🔑 PasswordEncoder\n密码编码器"]    A --> E["📦 AuthenticationManager\n认证管理器"]    A --> F["🎯 AuthenticationProvider\n认证提供者"]        B --> B1["用户名\n密码\n权限列表"]        C --> C1["用户名\n密码\n账户状态\n权限"]        D --> D1["BCryptPasswordEncoder\n（推荐）\nMD5PasswordEncoder\n（不安全）"]        E --> E1["委托给\nAuthenticationProvider"]        F --> F1["执行具体\n认证逻辑"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="5-2-密码加密与存储"><a href="#5-2-密码加密与存储" class="headerlink" title="5.2 密码加密与存储"></a>5.2 密码加密与存储</h3><p><strong>密码安全的重要性：</strong></p><p>密码绝对不能明文存储！即使数据库被攻击，攻击者也无法直接获取用户密码。</p><p><strong>推荐方案：BCrypt 加密</strong></p><p>BCrypt 的特点：</p><ul><li>🔐 <strong>自动加盐</strong>：每个密码都有不同的盐值，防止彩虹表攻击</li><li>🐢 <strong>计算缓慢</strong>：设计故意慢，增加暴力破解难度</li><li>✅ <strong>单向加密</strong>：无法逆向解密，只能验证</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 密码加密示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PasswordService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> PasswordEncoder passwordEncoder;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 加密密码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testEncode</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">rawPassword</span> <span class="operator">=</span> <span class="string">&quot;myPassword123&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// BCrypt 加密：每次加密结果都不同（因为随机盐）</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">encoded1</span> <span class="operator">=</span> passwordEncoder.encode(rawPassword);</span><br><span class="line">        <span class="type">String</span> <span class="variable">encoded2</span> <span class="operator">=</span> passwordEncoder.encode(rawPassword);</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;加密结果1：&quot;</span> + encoded1);</span><br><span class="line">        System.out.println(<span class="string">&quot;加密结果2：&quot;</span> + encoded2);</span><br><span class="line">        <span class="comment">// 输出示例：</span></span><br><span class="line">        <span class="comment">// 加密结果1：$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy</span></span><br><span class="line">        <span class="comment">// 加密结果2：$2a$10$v2G8M9Qv8Q8X9Q8X9Q8X9OefgHIJKLMNOPQRSTUVWX/abcd1234</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 虽然结果不同，但验证都是 true</span></span><br><span class="line">        System.out.println(<span class="string">&quot;验证1：&quot;</span> + passwordEncoder.matches(rawPassword, encoded1)); <span class="comment">// true</span></span><br><span class="line">        System.out.println(<span class="string">&quot;验证2：&quot;</span> + passwordEncoder.matches(rawPassword, encoded2)); <span class="comment">// true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-3-自定义登录接口"><a href="#5-3-自定义登录接口" class="headerlink" title="5.3 自定义登录接口"></a>5.3 自定义登录接口</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 登录请求参数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginRequest</span> &#123;</span><br><span class="line">    <span class="meta">@NotBlank(message = &quot;用户名不能为空&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@NotBlank(message = &quot;密码不能为空&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Boolean rememberMe;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 登录响应</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginResponse</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String token;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; roles;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> expireTime;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 登录 Controller</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AuthController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AuthenticationManager authenticationManager;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtService jwtService;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用户登录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/api/login&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;LoginResponse&gt; <span class="title function_">login</span><span class="params">(<span class="meta">@RequestBody</span> <span class="meta">@Valid</span> LoginRequest request)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 1. 使用 AuthenticationManager 进行认证</span></span><br><span class="line">            <span class="type">UsernamePasswordAuthenticationToken</span> <span class="variable">authenticationToken</span> <span class="operator">=</span> </span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">UsernamePasswordAuthenticationToken</span>(</span><br><span class="line">                    request.getUsername(), </span><br><span class="line">                    request.getPassword()</span><br><span class="line">                );</span><br><span class="line">            </span><br><span class="line">            <span class="type">Authentication</span> <span class="variable">authentication</span> <span class="operator">=</span> authenticationManager</span><br><span class="line">                .authenticate(authenticationToken);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 2. 获取认证信息</span></span><br><span class="line">            SecurityContextHolder.getContext().setAuthentication(authentication);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 3. 生成 JWT Token</span></span><br><span class="line">            <span class="type">UserDetails</span> <span class="variable">userDetails</span> <span class="operator">=</span> (UserDetails) authentication.getPrincipal();</span><br><span class="line">            <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> jwtService.generateToken(userDetails);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 4. 返回响应</span></span><br><span class="line">            <span class="type">LoginResponse</span> <span class="variable">response</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LoginResponse</span>();</span><br><span class="line">            response.setToken(token);</span><br><span class="line">            response.setUsername(userDetails.getUsername());</span><br><span class="line">            response.setRoles(userDetails.getAuthorities().stream()</span><br><span class="line">                .map(GrantedAuthority::getAuthority)</span><br><span class="line">                .collect(Collectors.toList()));</span><br><span class="line">            response.setExpireTime(jwtService.getExpireTime());</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> Result.success(response);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (BadCredentialsException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> Result.error(<span class="number">401</span>, <span class="string">&quot;用户名或密码错误&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (DisabledException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> Result.error(<span class="number">401</span>, <span class="string">&quot;账户已被禁用&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-4-多种认证方式配置"><a href="#5-4-多种认证方式配置" class="headerlink" title="5.4 多种认证方式配置"></a>5.4 多种认证方式配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多种认证方式配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .csrf(csrf -&gt; csrf.disable())</span><br><span class="line">            .authorizeHttpRequests(auth -&gt; auth</span><br><span class="line">                .anyRequest().authenticated()</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置多种认证方式</span></span><br><span class="line">            .authenticationProvider(customAuthenticationProvider())  <span class="comment">// 自定义认证</span></span><br><span class="line">            .formLogin(form -&gt; form</span><br><span class="line">                .loginProcessingUrl(<span class="string">&quot;/login&quot;</span>)  <span class="comment">// 处理表单登录</span></span><br><span class="line">            )</span><br><span class="line">            .httpBasic(basic -&gt; basic)  <span class="comment">// HTTP Basic 认证</span></span><br><span class="line">            .oauth2Login(oauth2 -&gt; oauth  <span class="comment">// OAuth2 登录</span></span><br><span class="line">                .authorizationEndpoint()</span><br><span class="line">                    .baseUri(<span class="string">&quot;/oauth2/authorize&quot;</span>)</span><br><span class="line">            )</span><br><span class="line">            .rememberMe(remember -&gt; remember  <span class="comment">// 记住我功能</span></span><br><span class="line">                .tokenValiditySeconds(<span class="number">86400</span>)  <span class="comment">// 7天</span></span><br><span class="line">                .rememberMeParameter(<span class="string">&quot;remember-me&quot;</span>)</span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、授权与权限控制"><a href="#六、授权与权限控制" class="headerlink" title="六、授权与权限控制"></a>六、授权与权限控制</h2><h3 id="6-1-基于-URL-的权限控制"><a href="#6-1-基于-URL-的权限控制" class="headerlink" title="6.1 基于 URL 的权限控制"></a>6.1 基于 URL 的权限控制</h3><p>最常用的权限控制方式，通过配置 URL 规则来控制访问权限：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .authorizeHttpRequests(auth -&gt; auth</span><br><span class="line">                <span class="comment">// 1. 公开路径：无需认证</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/&quot;</span>, <span class="string">&quot;/home&quot;</span>, <span class="string">&quot;/login&quot;</span>, <span class="string">&quot;/register&quot;</span>).permitAll()</span><br><span class="line">                .requestMatchers(<span class="string">&quot;/css/**&quot;</span>, <span class="string">&quot;/js/**&quot;</span>, <span class="string">&quot;/images/**&quot;</span>).permitAll()</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 2. 角色专属路径</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/admin/**&quot;</span>).hasRole(<span class="string">&quot;ADMIN&quot;</span>)           <span class="comment">// ADMIN 角色</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/user/**&quot;</span>).hasAnyRole(<span class="string">&quot;USER&quot;</span>, <span class="string">&quot;ADMIN&quot;</span>)  <span class="comment">// USER 或 ADMIN</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/api/public/**&quot;</span>).hasAuthority(<span class="string">&quot;READ_PUBLIC&quot;</span>)</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 3. IP 白名单</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/inner/**&quot;</span>).hasIpAddress(<span class="string">&quot;192.168.1.100&quot;</span>)</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 4. 其他请求需要认证</span></span><br><span class="line">                .anyRequest().authenticated()</span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-2-基于方法的权限控制"><a href="#6-2-基于方法的权限控制" class="headerlink" title="6.2 基于方法的权限控制"></a>6.2 基于方法的权限控制</h3><p>在 Controller 或 Service 方法上使用注解进行权限控制：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableMethodSecurity</span>  <span class="comment">// 启用方法级别安全</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    <span class="comment">// 配置</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 方法级别权限控制示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要 ADMIN 角色</span></span><br><span class="line">    <span class="meta">@PreAuthorize(&quot;hasRole(&#x27;ADMIN&#x27;)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUser</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        userRepository.deleteById(userId);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要特定权限</span></span><br><span class="line">    <span class="meta">@PreAuthorize(&quot;hasAuthority(&#x27;USER_READ&#x27;)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUserById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 复杂表达式：同时满足多个条件</span></span><br><span class="line">    <span class="meta">@PreAuthorize(&quot;#username == authentication.name or hasRole(&#x27;ADMIN&#x27;)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateProfile</span><span class="params">(String username, String profile)</span> &#123;</span><br><span class="line">        <span class="comment">// 只能修改自己的资料，或者你是管理员</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 基于权限集合</span></span><br><span class="line">    <span class="meta">@PreAuthorize(&quot;hasAnyAuthority(&#x27;USER_READ&#x27;, &#x27;USER_WRITE&#x27;)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">listUsers</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.findAll();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-权限注解详解"><a href="#6-3-权限注解详解" class="headerlink" title="6.3 权限注解详解"></a>6.3 权限注解详解</h3><table><thead><tr><th>注解</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>@PreAuthorize</strong></td><td>方法执行前检查权限</td><td><code>@PreAuthorize(&quot;hasRole(&#39;ADMIN&#39;)&quot;)</code></td></tr><tr><td><strong>@PostAuthorize</strong></td><td>方法执行后检查权限</td><td><code>@PostAuthorize(&quot;returnObject.owner == authentication.name&quot;)</code></td></tr><tr><td><strong>@Secured</strong></td><td>基于角色的检查</td><td><code>@Secured(&quot;ROLE_ADMIN&quot;)</code></td></tr><tr><td><strong>@RolesAllowed</strong></td><td>JSR-250 标准注解</td><td><code>@RolesAllowed(&quot;ADMIN&quot;)</code></td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 权限表达式详解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 角色检查</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;hasRole(&#x27;ADMIN&#x27;)&quot;)</span>                        <span class="comment">// 需要 ADMIN 角色</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;hasAnyRole(&#x27;ADMIN&#x27;, &#x27;USER&#x27;)&quot;)</span>              <span class="comment">// 需要其中之一</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 权限检查</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;hasAuthority(&#x27;USER_READ&#x27;)&quot;)</span>                <span class="comment">// 需要 READ 权限</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;hasAnyAuthorities(&#x27;USER_READ&#x27;, &#x27;USER_WRITE&#x27;)&quot;)</span>  <span class="comment">// 需要其中之一</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义安全表达式</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;@securityService.isOwner(#userId)&quot;)</span>        <span class="comment">// 调用 Bean 的方法</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;#user.name == authentication.name&quot;)</span>        <span class="comment">// 参数值比较</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 复杂条件</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;hasRole(&#x27;ADMIN&#x27;) or hasRole(&#x27;USER&#x27;)&quot;)</span>     <span class="comment">// 或条件</span></span><br><span class="line"><span class="meta">@PreAuthorize(&quot;hasRole(&#x27;ADMIN&#x27;) and #age &gt; 18&quot;)</span>          <span class="comment">// 且条件</span></span><br></pre></td></tr></table></figure><h3 id="6-4-权限不足时的异常处理"><a href="#6-4-权限不足时的异常处理" class="headerlink" title="6.4 权限不足时的异常处理"></a>6.4 权限不足时的异常处理</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 权限不足异常处理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestControllerAdvice</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityExceptionHandler</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 处理访问拒绝异常（权限不足）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(AccessDeniedException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleAccessDenied</span><span class="params">(AccessDeniedException e)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">403</span>, <span class="string">&quot;权限不足，无法访问该资源&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 处理认证失败异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(AuthenticationException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleAuthentication</span><span class="params">(AuthenticationException e)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">401</span>, <span class="string">&quot;认证失败，请先登录&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 处理会话过期异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(SessionAuthenticationException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleSessionExpired</span><span class="params">(SessionAuthenticationException e)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">401</span>, <span class="string">&quot;会话已过期，请重新登录&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、过滤器链详解"><a href="#七、过滤器链详解" class="headerlink" title="七、过滤器链详解"></a>七、过滤器链详解</h2><h3 id="7-1-Spring-Security-过滤器链"><a href="#7-1-Spring-Security-过滤器链" class="headerlink" title="7.1 Spring Security 过滤器链"></a>7.1 Spring Security 过滤器链</h3><p>Spring Security 通过一系列过滤器链来处理安全请求：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🔗 Security Filter Chain"] --> B["1️⃣ SecurityContext\nPersistence Filter"]    B --> C["2️⃣ WebAsyncManager\nIntegration Filter"]    C --> D["3️⃣ Header Writer Filter"]    D --> E["4️⃣ Cors Filter"]    E --> F["5️⃣ Csrf Filter"]    F --> G["6️⃣ UsernamePassword\nAuthentication Filter"]    G --> H["7️⃣ Basic Authentication Filter"]    H --> I["...\n其他过滤器"]    I --> J["8️⃣ FilterSecurity\nInterceptor"]        style A fill:#fff3e0    style B fill:#c8e6c9    style G fill:#c8e6c9    style J fill:#f8bbd0</pre></div><h3 id="7-2-核心过滤器详解"><a href="#7-2-核心过滤器详解" class="headerlink" title="7.2 核心过滤器详解"></a>7.2 核心过滤器详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 核心过滤器说明</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. SecurityContextPersistenceFilter</span></span><br><span class="line"><span class="comment">// 负责在请求开始时从 Session 加载 SecurityContext，请求结束时保存</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. UsernamePasswordAuthenticationFilter</span></span><br><span class="line"><span class="comment">// 拦截登录请求，提取用户名密码，调用 AuthenticationManager 认证</span></span><br><span class="line"><span class="comment">// 认证成功后创建 UsernamePasswordAuthenticationToken</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. BasicAuthenticationFilter</span></span><br><span class="line"><span class="comment">// 处理 HTTP Basic 认证，从 Authorization 头提取凭证</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. FilterSecurityInterceptor</span></span><br><span class="line"><span class="comment">// 核心过滤器，执行授权逻辑</span></span><br><span class="line"><span class="comment">// 根据配置决定是否允许访问</span></span><br></pre></td></tr></table></figure><h3 id="7-3-自定义过滤器"><a href="#7-3-自定义过滤器" class="headerlink" title="7.3 自定义过滤器"></a>7.3 自定义过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义过滤器示例：IP 白名单过滤器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IpWhiteListFilter</span> <span class="keyword">extends</span> <span class="title class_">OncePerRequestFilter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> List&lt;String&gt; WHITE_LIST = Arrays.asList(</span><br><span class="line">        <span class="string">&quot;127.0.0.1&quot;</span>, </span><br><span class="line">        <span class="string">&quot;192.168.1.100&quot;</span>,</span><br><span class="line">        <span class="string">&quot;localhost&quot;</span></span><br><span class="line">    );</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doFilterInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                    HttpServletResponse response,</span></span><br><span class="line"><span class="params">                                    FilterChain filterChain)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">clientIp</span> <span class="operator">=</span> getClientIp(request);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// IP 白名单检查</span></span><br><span class="line">        <span class="keyword">if</span> (WHITE_LIST.contains(clientIp)) &#123;</span><br><span class="line">            <span class="comment">// 在白名单中，直接放行</span></span><br><span class="line">            filterChain.doFilter(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 不在白名单，返回 403</span></span><br><span class="line">            response.setStatus(HttpServletResponse.SC_FORBIDDEN);</span><br><span class="line">            response.getWriter().write(<span class="string">&quot;&#123;\&quot;error\&quot;:\&quot;IP not allowed\&quot;&#125;&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">getClientIp</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">ip</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;X-Forwarded-For&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (ip == <span class="literal">null</span> || ip.isEmpty() || <span class="string">&quot;unknown&quot;</span>.equalsIgnoreCase(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;Proxy-Client-IP&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (ip == <span class="literal">null</span> || ip.isEmpty() || <span class="string">&quot;unknown&quot;</span>.equalsIgnoreCase(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;WL-Proxy-Client-IP&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (ip == <span class="literal">null</span> || ip.isEmpty() || <span class="string">&quot;unknown&quot;</span>.equalsIgnoreCase(ip)) &#123;</span><br><span class="line">            ip = request.getRemoteAddr();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ip;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 注册自定义过滤器到 Security 过滤链</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> IpWhiteListFilter ipWhiteListFilter;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .addFilterBefore(ipWhiteListFilter, UsernamePasswordAuthenticationFilter.class)</span><br><span class="line">            <span class="comment">// 其他配置...</span></span><br><span class="line">        ;</span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-4-过滤器执行顺序"><a href="#7-4-过滤器执行顺序" class="headerlink" title="7.4 过滤器执行顺序"></a>7.4 过滤器执行顺序</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 过滤器执行顺序配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            <span class="comment">// 1. 第一个执行：SecurityContext 持久化过滤器</span></span><br><span class="line">            <span class="comment">// 2. 添加自定义过滤器</span></span><br><span class="line">            .addFilterBefore(myCustomFilter(), UsernamePasswordAuthenticationFilter.class)</span><br><span class="line">            <span class="comment">// 3. 或者添加在某个过滤器之后</span></span><br><span class="line">            .addFilterAfter(myCustomFilter(), UsernamePasswordAuthenticationFilter.class)</span><br><span class="line">            <span class="comment">// 4. 替换某个过滤器</span></span><br><span class="line">            .withFilterChain(myCustomFilter(), DiscoveryFilterChain.class)</span><br><span class="line">            ;</span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、Security-实战：JWT-认证"><a href="#八、Security-实战：JWT-认证" class="headerlink" title="八、Security 实战：JWT 认证"></a>八、Security 实战：JWT 认证</h2><h3 id="8-1-JWT-简介"><a href="#8-1-JWT-简介" class="headerlink" title="8.1 JWT 简介"></a>8.1 JWT 简介</h3><p>JWT（JSON Web Token）是一种开放标准（RFC 7519），用于在各方之间<strong>安全传输信息</strong>。</p><p><strong>JWT 的结构：</strong></p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🎫 JWT 结构"] --> B["Header\n头部"]    A --> C["Payload\n载荷"]    A --> D["Signature\n签名"]        B --> B1["alg: 算法\ntyp: 类型"]        C --> C1["sub: 主题\niss: 签发者\nexp: 过期时间"]        D --> D1["HMAC SHA256\n(头部+载荷+密钥)"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0</pre></div><h3 id="8-2-JWT-工具类"><a href="#8-2-JWT-工具类" class="headerlink" title="8.2 JWT 工具类"></a>8.2 JWT 工具类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * JWT 工具类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// JWT 密钥（生产环境应从配置文件读取）</span></span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jwt.secret:mySecretKeyForJwtTokenGeneration123456789&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String secret;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 过期时间（毫秒）</span></span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jwt.expiration:86400000&#125;&quot;)</span>  <span class="comment">// 默认 24 小时</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> expiration;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 生成 Token</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">generateToken</span><span class="params">(UserDetails userDetails)</span> &#123;</span><br><span class="line">        Map&lt;String, Object&gt; claims = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 将用户名和权限列表存入 Token</span></span><br><span class="line">        claims.put(<span class="string">&quot;username&quot;</span>, userDetails.getUsername());</span><br><span class="line">        claims.put(<span class="string">&quot;authorities&quot;</span>, userDetails.getAuthorities().stream()</span><br><span class="line">            .map(GrantedAuthority::getAuthority)</span><br><span class="line">            .collect(Collectors.toList()));</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> Jwts.builder()</span><br><span class="line">            .setClaims(claims)</span><br><span class="line">            .setSubject(userDetails.getUsername())</span><br><span class="line">            .setIssuedAt(<span class="keyword">new</span> <span class="title class_">Date</span>())</span><br><span class="line">            .setExpiration(<span class="keyword">new</span> <span class="title class_">Date</span>(System.currentTimeMillis() + expiration))</span><br><span class="line">            .signWith(SignatureAlgorithm.HS256, secret.getBytes())</span><br><span class="line">            .compact();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 解析 Token 获取用户名</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getUsernameFromToken</span><span class="params">(String token)</span> &#123;</span><br><span class="line">        <span class="type">Claims</span> <span class="variable">claims</span> <span class="operator">=</span> parseToken(token);</span><br><span class="line">        <span class="keyword">return</span> claims.getSubject();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取过期时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">long</span> <span class="title function_">getExpireTime</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> expiration;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 验证 Token</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">validateToken</span><span class="params">(String token, UserDetails userDetails)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> getUsernameFromToken(token);</span><br><span class="line">        <span class="keyword">return</span> username.equals(userDetails.getUsername()) </span><br><span class="line">            &amp;&amp; !isTokenExpired(token);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 检查 Token 是否过期</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isTokenExpired</span><span class="params">(String token)</span> &#123;</span><br><span class="line">        <span class="type">Date</span> <span class="variable">expiration</span> <span class="operator">=</span> parseToken(token).getExpiration();</span><br><span class="line">        <span class="keyword">return</span> expiration.before(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 解析 Token</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Claims <span class="title function_">parseToken</span><span class="params">(String token)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Jwts.parser()</span><br><span class="line">            .setSigningKey(secret.getBytes())</span><br><span class="line">            .parseClaimsJws(token)</span><br><span class="line">            .getBody();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-JWT-认证过滤器"><a href="#8-3-JWT-认证过滤器" class="headerlink" title="8.3 JWT 认证过滤器"></a>8.3 JWT 认证过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * JWT 认证过滤器</span></span><br><span class="line"><span class="comment"> * 每次请求都检查 Token</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtAuthenticationFilter</span> <span class="keyword">extends</span> <span class="title class_">OncePerRequestFilter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtService jwtService;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDetailsService userDetailsService;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doFilterInternal</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                    HttpServletResponse response,</span></span><br><span class="line"><span class="params">                                    FilterChain filterChain)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 1. 从请求头获取 Token</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">jwt</span> <span class="operator">=</span> extractJwtFromRequest(request);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (jwt != <span class="literal">null</span> &amp;&amp; jwtService.validateToken(jwt)) &#123;</span><br><span class="line">                <span class="comment">// 2. 解析 Token 获取用户名</span></span><br><span class="line">                <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> jwtService.getUsernameFromToken(jwt);</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 3. 加载用户信息</span></span><br><span class="line">                <span class="type">UserDetails</span> <span class="variable">userDetails</span> <span class="operator">=</span> userDetailsService.loadUserByUsername(username);</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 4. 创建认证令牌</span></span><br><span class="line">                <span class="type">UsernamePasswordAuthenticationToken</span> <span class="variable">authentication</span> <span class="operator">=</span></span><br><span class="line">                    <span class="keyword">new</span> <span class="title class_">UsernamePasswordAuthenticationToken</span>(</span><br><span class="line">                        userDetails,</span><br><span class="line">                        <span class="literal">null</span>,</span><br><span class="line">                        userDetails.getAuthorities()</span><br><span class="line">                    );</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 5. 设置认证信息到上下文</span></span><br><span class="line">                authentication.setDetails(</span><br><span class="line">                    <span class="keyword">new</span> <span class="title class_">WebAuthenticationDetailsSource</span>().buildDetails(request)</span><br><span class="line">                );</span><br><span class="line">                </span><br><span class="line">                SecurityContextHolder.getContext().setAuthentication(authentication);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            logger.error(<span class="string">&quot;JWT 认证失败：&quot;</span> + e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 6. 继续执行后续过滤器</span></span><br><span class="line">        filterChain.doFilter(request, response);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从请求头提取 JWT</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">extractJwtFromRequest</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">bearerToken</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;Authorization&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (bearerToken != <span class="literal">null</span> &amp;&amp; bearerToken.startsWith(<span class="string">&quot;Bearer &quot;</span>)) &#123;</span><br><span class="line">            <span class="keyword">return</span> bearerToken.substring(<span class="number">7</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-4-完整的-JWT-认证配置"><a href="#8-4-完整的-JWT-认证配置" class="headerlink" title="8.4 完整的 JWT 认证配置"></a>8.4 完整的 JWT 认证配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 完整的 Security + JWT 配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="meta">@EnableMethodSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtAuthenticationFilter jwtAuthenticationFilter;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDetailsService userDetailsService;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            <span class="comment">// 禁用 CSRF</span></span><br><span class="line">            .csrf(csrf -&gt; csrf.disable())</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置授权规则</span></span><br><span class="line">            .authorizeHttpRequests(auth -&gt; auth</span><br><span class="line">                .requestMatchers(<span class="string">&quot;/api/auth/**&quot;</span>).permitAll()  <span class="comment">// 公开登录接口</span></span><br><span class="line">                .requestMatchers(<span class="string">&quot;/api/public/**&quot;</span>).permitAll()  <span class="comment">// 公开接口</span></span><br><span class="line">                .anyRequest().authenticated()</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置会话管理</span></span><br><span class="line">            .sessionManagement(session -&gt; session</span><br><span class="line">                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)</span><br><span class="line">                <span class="comment">// JWT 认证使用无状态会话</span></span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 添加 JWT 过滤器</span></span><br><span class="line">            .addFilterBefore(jwtAuthenticationFilter, </span><br><span class="line">                UsernamePasswordAuthenticationFilter.class)</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 配置异常处理</span></span><br><span class="line">            .exceptionHandling(ex -&gt; ex</span><br><span class="line">                .authenticationEntryPoint((request, response, authException) -&gt; &#123;</span><br><span class="line">                    response.setStatus(<span class="number">401</span>);</span><br><span class="line">                    response.setContentType(<span class="string">&quot;application/json;charset=UTF-8&quot;</span>);</span><br><span class="line">                    response.getWriter().write(<span class="string">&quot;&#123;\&quot;error\&quot;:\&quot;请先登录\&quot;&#125;&quot;</span>);</span><br><span class="line">                &#125;)</span><br><span class="line">                .accessDeniedHandler((request, response, accessDeniedException) -&gt; &#123;</span><br><span class="line">                    response.setStatus(<span class="number">403</span>);</span><br><span class="line">                    response.setContentType(<span class="string">&quot;application/json;charset=UTF-8&quot;</span>);</span><br><span class="line">                    response.getWriter().write(<span class="string">&quot;&#123;\&quot;error\&quot;:\&quot;权限不足\&quot;&#125;&quot;</span>);</span><br><span class="line">                &#125;)</span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> PasswordEncoder <span class="title function_">passwordEncoder</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">BCryptPasswordEncoder</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、OAuth2-第三方登录"><a href="#九、OAuth2-第三方登录" class="headerlink" title="九、OAuth2 第三方登录"></a>九、OAuth2 第三方登录</h2><h3 id="9-1-OAuth2-简介"><a href="#9-1-OAuth2-简介" class="headerlink" title="9.1 OAuth2 简介"></a>9.1 OAuth2 简介</h3><p>OAuth2 是一个<strong>授权框架</strong>，允许第三方应用在用户授权的情况下访问用户在另一个服务上的资源，而无需提供用户名密码。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🌐 OAuth2 授权流程"] --> B["1️⃣ 用户点击\n第三方登录"]    B --> C["2️⃣ 跳转授权页面\n用户授权"]    C --> D["3️⃣ 返回授权码"]    D --> E["4️⃣ 后端用授权码\n换取 Access Token"]    E --> F["5️⃣ 用 Token\n获取用户信息"]        style A fill:#fff3e0    style C fill:#c8e6c9    style E fill:#c8e6c9</pre></div><h3 id="9-2-Spring-Security-OAuth2-配置"><a href="#9-2-Spring-Security-OAuth2-配置" class="headerlink" title="9.2 Spring Security OAuth2 配置"></a>9.2 Spring Security OAuth2 配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml 配置 OAuth2</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">security:</span></span><br><span class="line">    <span class="attr">oauth2:</span></span><br><span class="line">      <span class="attr">client:</span></span><br><span class="line">        <span class="attr">registration:</span></span><br><span class="line">          <span class="attr">github:</span></span><br><span class="line">            <span class="attr">client-id:</span> <span class="string">your-client-id</span></span><br><span class="line">            <span class="attr">client-secret:</span> <span class="string">your-client-secret</span></span><br><span class="line">            <span class="attr">scope:</span> <span class="string">read:user,</span> <span class="string">user:email</span></span><br><span class="line">          <span class="attr">google:</span></span><br><span class="line">            <span class="attr">client-id:</span> <span class="string">your-client-id</span></span><br><span class="line">            <span class="attr">client-secret:</span> <span class="string">your-client-secret</span></span><br><span class="line">            <span class="attr">scope:</span> <span class="string">profile,</span> <span class="string">email</span></span><br><span class="line">        <span class="attr">provider:</span></span><br><span class="line">          <span class="attr">github:</span></span><br><span class="line">            <span class="attr">authorization-uri:</span> <span class="string">https://github.com/login/oauth/authorize</span></span><br><span class="line">            <span class="attr">token-uri:</span> <span class="string">https://github.com/login/oauth/access_token</span></span><br><span class="line">            <span class="attr">user-info-uri:</span> <span class="string">https://api.github.com/user</span></span><br><span class="line">            <span class="attr">user-name-attribute:</span> <span class="string">login</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * OAuth2 安全配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OAuth2SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .csrf(csrf -&gt; csrf.disable())</span><br><span class="line">            .authorizeHttpRequests(auth -&gt; auth</span><br><span class="line">                .requestMatchers(<span class="string">&quot;/login/**&quot;</span>, <span class="string">&quot;/oauth2/**&quot;</span>).permitAll()</span><br><span class="line">                .anyRequest().authenticated()</span><br><span class="line">            )</span><br><span class="line">            .oauth2Login(oauth2 -&gt; oauth2</span><br><span class="line">                .authorizationEndpoint()</span><br><span class="line">                    .baseUri(<span class="string">&quot;/oauth2/authorize&quot;</span>)</span><br><span class="line">            )</span><br><span class="line">            .oauth2Client(oauth2 -&gt; oauth2</span><br><span class="line">                .authorizationServer()</span><br><span class="line">                    .clientRegistrationRepository(...)</span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * OAuth2 登录成功处理器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OAuth2AuthenticationSuccessHandler</span> </span><br><span class="line">    <span class="keyword">extends</span> <span class="title class_">SimpleUrlAuthenticationSuccessHandler</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService userService;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onAuthenticationSuccess</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                                        HttpServletResponse response,</span></span><br><span class="line"><span class="params">                                        Authentication authentication)</span> </span><br><span class="line">            <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">OAuth2AuthenticationToken</span> <span class="variable">authToken</span> <span class="operator">=</span> </span><br><span class="line">            (OAuth2AuthenticationToken) authentication;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取第三方提供商信息</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">provider</span> <span class="operator">=</span> authToken.getAuthorizedClientRegistrationId();</span><br><span class="line">        <span class="type">String</span> <span class="variable">providerId</span> <span class="operator">=</span> authToken.getgetName();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 查找或创建用户</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userService.findOrCreateByOAuth2(provider, providerId);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 生成 JWT Token（省略）</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> jwtService.generateToken(user);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 返回 Token</span></span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;&#123;\&quot;token\&quot;:\&quot;&quot;</span> + token + <span class="string">&quot;\&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十、Spring-Security-高级特性"><a href="#十、Spring-Security-高级特性" class="headerlink" title="十、Spring Security 高级特性"></a>十、Spring Security 高级特性</h2><h3 id="10-1-会话管理"><a href="#10-1-会话管理" class="headerlink" title="10.1 会话管理"></a>10.1 会话管理</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 会话管理配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .sessionManagement(session -&gt; session</span><br><span class="line">                <span class="comment">// 1. 会话固定攻击防护</span></span><br><span class="line">                .sessionFixation()</span><br><span class="line">                    .changeSessionId()  <span class="comment">// 创建新会话，销毁旧会话</span></span><br><span class="line">                    <span class="comment">// 其他选项：migrateSession、none、newSession</span></span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 2. 同一用户最大会话数</span></span><br><span class="line">                .maximumSessions(<span class="number">1</span>)</span><br><span class="line">                    .maxSessionsPreventsLogin(<span class="literal">false</span>)  <span class="comment">// 超过后阻止新登录</span></span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 3. 会话超时时间</span></span><br><span class="line">                .expireUrl(<span class="string">&quot;/login?expired&quot;</span>)</span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="10-2-CSRF-防护"><a href="#10-2-CSRF-防护" class="headerlink" title="10.2 CSRF 防护"></a>10.2 CSRF 防护</h3><p>CSRF（Cross-Site Request Forgery）跨站请求伪造，攻击者诱导用户点击链接，自动执行恶意请求。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🚫 CSRF 攻击原理"] --> B["1️⃣ 用户已登录\n银行网站"]    B --> C["2️⃣ 用户被诱导\n点击恶意链接"]    C --> D["3️⃣ 浏览器自动发送请求\n（携带 Cookie）"]    D --> E["4️⃣ 银行误以为是\n用户操作"]    E --> F["💰 转账到攻击者账户"]        style A fill:#fff3e0    style F fill:#ffcdd2</pre></div><p><strong>解决方案：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            <span class="comment">// 1. 启用 CSRF 防护（默认）</span></span><br><span class="line">            .csrf(csrf -&gt; csrf</span><br><span class="line">                .ignoringRequestMatchers(<span class="string">&quot;/api/**&quot;</span>)  <span class="comment">// 前后端分离 API 禁用</span></span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 2. 配置 CSRF Token 传递方式</span></span><br><span class="line">            .csrf(csrf -&gt; csrf</span><br><span class="line">                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())</span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 3. 前端需要从 Cookie 或 Header 中获取 CSRF Token</span></span><br><span class="line">            <span class="comment">// 并在请求时一起发送</span></span><br><span class="line">        ;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="10-3-密码加密选项"><a href="#10-3-密码加密选项" class="headerlink" title="10.3 密码加密选项"></a>10.3 密码加密选项</h3><table><thead><tr><th>加密方式</th><th>说明</th><th>推荐度</th></tr></thead><tbody><tr><td><strong>BCrypt</strong></td><td>安全可靠，自动加盐，推荐</td><td>✅✅✅</td></tr><tr><td><strong>Argon2</strong></td><td>最新标准，安全性高，Java 11+</td><td>✅✅</td></tr><tr><td><strong>PBKDF2</strong></td><td>较老但安全</td><td>✅✅</td></tr><tr><td><strong>MD5&#x2F;SHA1</strong></td><td>不安全，已被淘汰</td><td>❌</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PasswordEncoderConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> PasswordEncoder <span class="title function_">passwordEncoder</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 1. BCrypt（推荐）</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">BCryptPasswordEncoder</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. Argon2（需要额外依赖）</span></span><br><span class="line">        <span class="comment">// return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. 多种编码器组合</span></span><br><span class="line">        <span class="keyword">return</span> PasswordEncoderFactories.createDelegatingPasswordEncoder();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="10-4-记住我功能"><a href="#10-4-记住我功能" class="headerlink" title="10.4 记住我功能"></a>10.4 记住我功能</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 记住我功能配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        http</span><br><span class="line">            .rememberMe(remember -&gt; remember</span><br><span class="line">                .key(<span class="string">&quot;uniqueAndSecretKey&quot;</span>)           <span class="comment">// Token 密钥</span></span><br><span class="line">                .tokenValiditySeconds(<span class="number">86400</span> * <span class="number">7</span>)    <span class="comment">// 7 天有效期</span></span><br><span class="line">                .rememberMeParameter(<span class="string">&quot;remember-me&quot;</span>) <span class="comment">// 表单参数名</span></span><br><span class="line">                .useSecureCookie(<span class="literal">false</span>)             <span class="comment">// 生产环境应设为 true</span></span><br><span class="line">            );</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> http.build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十一、常见问题与最佳实践"><a href="#十一、常见问题与最佳实践" class="headerlink" title="十一、常见问题与最佳实践"></a>十一、常见问题与最佳实践</h2><h3 id="11-1-常见问题与解决方案"><a href="#11-1-常见问题与解决方案" class="headerlink" title="11.1 常见问题与解决方案"></a>11.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["⚠️ 401 未认证"]    A --> C["⚠️ 403 权限不足"]    A --> D["⚠️ CSRF 错误"]    A --> E["⚠️ Session 过期"]        B --> B1["未登录或 Token 无效"]    B1 --> B2["检查请求头\nAuthorization"]        C --> C1["用户无权限访问"]    C1 --> C2["检查角色配置\n权限注解"]        D --> D1["CSRF Token 不匹配"]    D1 --> D2["前端添加\nCSRF Token"]        E --> E1["会话已失效"]    E1 --> E2["重新登录\n或延长超时"]        style A fill:#fff3e0</pre></div><h3 id="11-2-安全配置清单"><a href="#11-2-安全配置清单" class="headerlink" title="11.2 安全配置清单"></a>11.2 安全配置清单</h3><table><thead><tr><th>安全实践</th><th>说明</th><th>推荐程度</th></tr></thead><tbody><tr><td><strong>使用 HTTPS</strong></td><td>生产环境必须启用</td><td>✅✅✅</td></tr><tr><td><strong>密码加密</strong></td><td>使用 BCrypt 等安全算法</td><td>✅✅✅</td></tr><tr><td><strong>CSRF 防护</strong></td><td>前后端分离项目可禁用 API 端点</td><td>✅✅</td></tr><tr><td><strong>XSS 防护</strong></td><td>输入校验、输出编码</td><td>✅✅✅</td></tr><tr><td><strong>SQL 注入防护</strong></td><td>使用参数化查询</td><td>✅✅✅</td></tr><tr><td><strong>JWT 短期失效</strong></td><td>Token 过期时间不宜过长</td><td>✅✅</td></tr><tr><td><strong>敏感信息脱敏</strong></td><td>日志中不打印密码等信息</td><td>✅✅</td></tr></tbody></table><h3 id="11-3-生产环境配置建议"><a href="#11-3-生产环境配置建议" class="headerlink" title="11.3 生产环境配置建议"></a>11.3 生产环境配置建议</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生产环境安全配置建议</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">security:</span></span><br><span class="line">    <span class="attr">oauth2:</span></span><br><span class="line">      <span class="attr">resourceserver:</span></span><br><span class="line">        <span class="attr">jwt:</span></span><br><span class="line">          <span class="attr">issuer-uri:</span> <span class="string">$&#123;JWT_ISSUER_URI&#125;</span>  <span class="comment"># 从环境变量读取</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不在前端暴露敏感配置</span></span><br><span class="line"><span class="comment"># 使用环境变量或配置中心</span></span><br></pre></td></tr></table></figure><hr><h2 id="十二、总结"><a href="#十二、总结" class="headerlink" title="十二、总结"></a>十二、总结</h2><h3 id="12-1-核心知识点回顾"><a href="#12-1-核心知识点回顾" class="headerlink" title="12.1 核心知识点回顾"></a>12.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Spring Security))    认证      表单登录      HTTP Basic      JWT Token      OAuth2      LDAP    授权      URL 权限控制      方法级别权限      角色 vs 权限      @PreAuthorize    过滤器      Security Filter Chain      自定义过滤器      过滤器顺序    安全防护      CSRF 防护      会话管理      密码加密      XSS 防护    高级特性      JWT 认证      OAuth2 登录      记住我      跨域配置</pre></div><h3 id="12-2-学习路线建议"><a href="#12-2-学习路线建议" class="headerlink" title="12.2 学习路线建议"></a>12.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["学习路线"] --> B["第一阶段\n基础配置"]    B --> C["第二阶段\n用户认证"]    C --> D["第三阶段\n权限控制"]    D --> E["第四阶段\nJWT 集成"]    E --> F["第五阶段\nOAuth2"]        B --> B1["基本配置\n公开路径"]    B --> B2["自定义登录"]        C --> C1["UserDetailsService\n密码加密"]        D --> D1["URL 权限\n方法权限"]        E --> E1["Token 生成\n过滤器"]        F --> F1["第三方登录\n资源服务器"]        style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0    style F fill:#e3f2fd</pre></div><h3 id="12-3-下一步推荐学习"><a href="#12-3-下一步推荐学习" class="headerlink" title="12.3 下一步推荐学习"></a>12.3 下一步推荐学习</h3><ul><li>📖 <strong>Spring Security 源码</strong>：深入理解框架设计</li><li>📖 <strong>OAuth2 协议规范</strong>：深入理解授权流程</li><li>📖 <strong>Spring Cloud Security</strong>：微服务安全方案</li><li>📖 <strong>Shiro 安全框架</strong>：对比学习另一安全框架</li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：安全是软件开发中不可忽视的重要环节。Spring Security 提供了完善的安全解决方案，但正确的配置和使用同样重要。希望本文能帮助你建立完整的安全知识体系，在实际项目中有效保护你的应用安全！🔐</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/spring-security-guide/</id>
    <link href="https://blog.codenav.top/spring-security-guide/"/>
    <published>2026-05-24T11:06:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Spring-Security-安全框架详解：从入门到实战-🔐"><a href="#Spring-Security-安全框架详解：从入门到实战-🔐" class="headerlink" title="Spring Security 安全框架详解：从入门到实]]>
    </summary>
    <title>Spring Security 安全框架详解：从入门到实战 🔐</title>
    <updated>2026-05-24T11:08:04.698Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="Spring" scheme="https://blog.codenav.top/tags/Spring/"/>
    <category term="框架" scheme="https://blog.codenav.top/tags/%E6%A1%86%E6%9E%B6/"/>
    <category term="IoC" scheme="https://blog.codenav.top/tags/IoC/"/>
    <category term="AOP" scheme="https://blog.codenav.top/tags/AOP/"/>
    <category term="Spring Boot" scheme="https://blog.codenav.top/tags/Spring-Boot/"/>
    <content>
      <![CDATA[<h1 id="Spring-框架全家桶详解：从入门到实战-🌱"><a href="#Spring-框架全家桶详解：从入门到实战-🌱" class="headerlink" title="Spring 框架全家桶详解：从入门到实战 🌱"></a>Spring 框架全家桶详解：从入门到实战 🌱</h1><blockquote><p>Spring 是 Java 领域最强大的企业级应用开发框架，它的出现彻底改变了 Java 企业级开发的格局。从最初的 Spring Framework 到现在的 Spring Boot、Spring Cloud，Spring 生态已经成为 Java 开发者必须掌握的核心技术。本文将带你全面理解 Spring 的核心概念、核心功能以及实战应用！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80spring-%E6%A6%82%E8%BF%B0%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-spring">一、Spring 概述：为什么选择 Spring？</a></li><li><a href="#%E4%BA%8Cspring-ioc-%E5%AE%B9%E5%99%A8%E4%B8%8E%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5">二、Spring IoC 容器与依赖注入</a></li><li><a href="#%E4%B8%89spring-bean-%E7%AE%A1%E7%90%86%E8%AF%A6%E8%A7%A3">三、Spring Bean 管理详解</a></li><li><a href="#%E5%9B%9Bspring-aop-%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B">四、Spring AOP 面向切面编程</a></li><li><a href="#%E4%BA%94spring-%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86">五、Spring 事务管理</a></li><li><a href="#%E5%85%ADspring-mvc-%E5%BC%80%E5%8F%91web%E5%BA%94%E7%94%A8">六、Spring MVC 开发Web应用</a></li><li><a href="#%E4%B8%83spring-boot-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%8F%91">七、Spring Boot 快速开发</a></li><li><a href="#%E5%85%ABspring-boot-%E6%A0%B8%E5%BF%83%E9%85%8D%E7%BD%AE">八、Spring Boot 核心配置</a></li><li><a href="#%E4%B9%9Dspring-data-%E7%AE%80%E5%8C%96%E6%95%B0%E6%8D%AE%E8%AE%BF%E9%97%AE">九、Spring Data 简化数据访问</a></li><li><a href="#%E5%8D%81spring-boot-%E6%97%A5%E5%BF%97%E4%B8%8E%E7%9B%91%E6%8E%A7">十、Spring Boot 日志与监控</a></li><li><a href="#%E5%8D%81%E4%B8%80%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十一、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%BA%8C%E6%80%BB%E7%BB%93">十二、总结</a></li></ul><hr><h2 id="一、Spring-概述：为什么选择-Spring？"><a href="#一、Spring-概述：为什么选择-Spring？" class="headerlink" title="一、Spring 概述：为什么选择 Spring？"></a>一、Spring 概述：为什么选择 Spring？</h2><h3 id="1-1-Spring-的诞生背景"><a href="#1-1-Spring-的诞生背景" class="headerlink" title="1.1 Spring 的诞生背景"></a>1.1 Spring 的诞生背景</h3><p>2002 年，Rod Johnson 在其著作《Expert One-on-One J2EE Design and Development》中首次提出了 Spring 的概念。此时的 Java 企业级开发主要依赖 EJB（Enterprise JavaBeans）框架，但 EJB 存在以下问题：</p><p><strong>EJB 的痛点：</strong></p><ul><li>📦 <strong>重量级</strong>：每个 Bean 都需要实现特定的接口（如 SessionBean、EntityBean）</li><li>🔧 <strong>配置复杂</strong>：大量的 XML 配置，部署描述符（DD）冗长</li><li>🐢 <strong>性能低下</strong>：EJB 容器启动慢，Bean 实例化消耗资源多</li><li>🧪 <strong>难以测试</strong>：业务逻辑与容器紧耦合，单元测试困难</li></ul><p><strong>Spring 的创新：</strong><br>Rod Johnson 认为，我们可以采用<strong>简单的 JavaBean（Plain Old Java Object，简称 POJO）</strong>来代替 EJB，实现轻量级的企业级开发。Spring 通过依赖注入（DI）和面向切面编程（AOP）等核心功能，让 Java 开发变得简单、灵活、可测试。</p><h3 id="1-2-什么是-Spring？"><a href="#1-2-什么是-Spring？" class="headerlink" title="1.2 什么是 Spring？"></a>1.2 什么是 Spring？</h3><p>简单来说，<strong>Spring 是一个轻量级的 Java 开发框架</strong>，它提供了从基础配置到企业级应用的完整解决方案。</p><p>我们可以把 Spring 比喻成一个<strong>大型工具箱</strong>：</p><ul><li>🔨 它提供了各种工具（模块），你可以根据需要选择使用</li><li>📦 这些工具都是精心设计、互相配合的</li><li>🔧 你不需要自己制造轮子，Spring 已经帮你做好了一切准备</li></ul><h3 id="1-3-Spring-的核心优势"><a href="#1-3-Spring-的核心优势" class="headerlink" title="1.3 Spring 的核心优势"></a>1.3 Spring 的核心优势</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["☕️ Spring 框架优势"] --> B["🔓 低侵入性"]    A --> C["🧩 组件化设计"]    A --> D["📦 强大生态"]    A --> E["🧪 易于测试"]    A --> F["🔄 灵活扩展"]        B --> B1["不强制实现接口\n保持代码纯净"]        C --> C1["核心模块独立\n按需引入"]        D --> D1["Spring Boot\nSpring Cloud\nSpring Security"]        E --> E1["依赖注入\nMock 测试"]        F --> F1["第三方框架\n无缝集成"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#f8bbd0    style F fill:#fff3e0</pre></div><p>让我详细解释 Spring 的四大核心优势：</p><p><strong>1️⃣ 低侵入性 —— 保持代码纯净</strong></p><p>传统的 EJB 框架要求开发者实现特定的接口（如 <code>SessionBean</code>），这会让你的业务代码充满框架特定的代码。而 Spring 不要求你实现任何特定接口，你的类可以保持 Plain Old Java Object（POJO）的形态。这意味着：</p><ul><li>你的代码更容易理解</li><li>你的代码更容易在其他项目中使用</li><li>你可以在任何时候去掉 Spring，而不改变业务代码</li></ul><p><strong>2️⃣ 组件化设计 —— 按需引入</strong></p><p>Spring 采用了模块化的设计理念，主要模块包括：</p><ul><li><code>spring-core</code>：核心工具类</li><li><code>spring-beans</code>：Bean 的创建和管理</li><li><code>spring-context</code>：应用上下文</li><li><code>spring-aop</code>：面向切面编程</li><li><code>spring-web</code>：Web 开发支持</li><li><code>spring-tx</code>：事务管理</li></ul><p>你可以根据项目需要，只引入需要的模块，而不需要加载整个框架。</p><p><strong>3️⃣ 强大生态 —— 一站式解决方案</strong></p><p>Spring 生态非常丰富，包括：</p><ul><li><strong>Spring Boot</strong>：简化 Spring 应用开发</li><li><strong>Spring Cloud</strong>：微服务架构解决方案</li><li><strong>Spring Security</strong>：安全认证授权</li><li><strong>Spring Data</strong>：数据访问统一抽象</li><li><strong>Spring Batch</strong>：批处理框架</li><li><strong>Spring Integration</strong>：消息集成</li></ul><p><strong>4️⃣ 易于测试 —— 可维护性高</strong></p><p>Spring 的依赖注入让对象之间的依赖关系变得清晰，你可以轻松使用 Mock 对象进行单元测试，而不需要启动整个容器。</p><h3 id="1-4-Spring-框架模块全景"><a href="#1-4-Spring-框架模块全景" class="headerlink" title="1.4 Spring 框架模块全景"></a>1.4 Spring 框架模块全景</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📦 Spring Framework"] --> B["🌱 Spring Core\n核心容器"]    A --> C["🧩 Spring AOP\n面向切面"]    A --> D["📝 Spring DAO\n数据访问"]    A --> E["🌐 Spring Web\nWeb 开发"]    A --> F["🧪 Spring Test\n测试框架"]        B --> B1["IoC/DI"]    B --> B2["BeanFactory"]    B --> B3["ApplicationContext"]        D --> D1["JDBC Template"]    D --> D2["事务管理"]        E --> E1["Spring MVC"]    E --> E2["WebSocket"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0    style E fill:#f8bbd0    style F fill:#e3f2fd</pre></div><p><strong>各模块详细介绍：</strong></p><table><thead><tr><th>模块</th><th>功能</th><th>核心类&#x2F;接口</th></tr></thead><tbody><tr><td><strong>Spring Core</strong></td><td>核心容器，提供 IoC 和 DI 功能</td><td>BeanFactory, ApplicationContext</td></tr><tr><td><strong>Spring AOP</strong></td><td>面向切面编程，支持事务、日志等功能</td><td>Aspect, JoinPoint, Advice</td></tr><tr><td><strong>Spring DAO</strong></td><td>数据访问，简化 JDBC 操作</td><td>JdbcTemplate, TransactionManager</td></tr><tr><td><strong>Spring Web</strong></td><td>Web 开发支持</td><td>DispatcherServlet, Controller</td></tr><tr><td><strong>Spring Test</strong></td><td>单元测试支持</td><td>SpringRunner, MockMvc</td></tr></tbody></table><h3 id="1-5-Spring-vs-Java-EE-对比"><a href="#1-5-Spring-vs-Java-EE-对比" class="headerlink" title="1.5 Spring vs Java EE 对比"></a>1.5 Spring vs Java EE 对比</h3><table><thead><tr><th>对比维度</th><th>Spring</th><th>Java EE (Jakarta EE)</th></tr></thead><tbody><tr><td><strong>学习曲线</strong></td><td>平缓，容易上手</td><td>陡峭，需要学习大量规范</td></tr><tr><td><strong>配置方式</strong></td><td>灵活（注解&#x2F;Java Config&#x2F;XML）</td><td>规范但复杂</td></tr><tr><td><strong>侵入性</strong></td><td>低侵入，保持 POJO</td><td>较高侵入</td></tr><tr><td><strong>轻量级</strong></td><td>✅ 轻量级容器</td><td>重量级容器</td></tr><tr><td><strong>灵活性</strong></td><td>高度灵活</td><td>规范性更强</td></tr><tr><td><strong>生态</strong></td><td>极其丰富</td><td>逐渐追赶</td></tr><tr><td><strong>社区活跃度</strong></td><td>非常活跃</td><td>一般</td></tr></tbody></table><hr><h2 id="二、Spring-IoC-容器与依赖注入"><a href="#二、Spring-IoC-容器与依赖注入" class="headerlink" title="二、Spring IoC 容器与依赖注入"></a>二、Spring IoC 容器与依赖注入</h2><h3 id="2-1-什么是-IoC？"><a href="#2-1-什么是-IoC？" class="headerlink" title="2.1 什么是 IoC？"></a>2.1 什么是 IoC？</h3><p><strong>IoC（Inversion of Control）控制反转</strong>，是 Spring 的核心设计思想，也是面向对象设计原则中<strong>依赖倒置原则</strong>的具体实现。</p><p>要理解 IoC，我们需要先了解<strong>传统编程方式的问题</strong>：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["❌ 传统方式"] --> A1["程序主动\nnew 对象"]    A1 --> A2["对象间耦合\n难以测试"]        B["✅ IoC 方式"] --> B1["容器创建\n管理对象"]    B1 --> B2["松耦合\n易测试"]        style A fill:#ffcdd2    style B fill:#c8e6c9</pre></div><p><strong>传统方式的问题举例：</strong></p><p>假设我们有一个 <code>UserService</code> 类需要使用 <code>UserDao</code> 来操作数据库：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 传统方式：UserService 自己创建 UserDao</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">UserDao</span> <span class="variable">userDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UserDao</span>();  <span class="comment">// ❌ 主动创建依赖</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        userDao.save(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种方式的<strong>问题</strong>是：</p><ol><li><code>UserService</code> 和 <code>UserDao</code> 紧耦合，无法替换成其他实现</li><li>如果 <code>UserDao</code> 的构造函数变了（比如需要参数），<code>UserService</code> 也需要改</li><li>单元测试时，无法用 Mock 对象替换真实的 <code>UserDao</code></li></ol><p><strong>IoC 的解决思路：</strong></p><p>IoC 的核心思想是**”不要让类自己创建依赖，而是让外部（容器）把依赖传进来”**。</p><p>打个比方：</p><ul><li>🎬 <strong>传统方式</strong>：你（在餐厅）自己做饭</li><li>🍽️ <strong>IoC 方式</strong>：你（在餐厅）点菜，由厨房（容器）做好端给你</li></ul><h3 id="2-2-什么是-DI？"><a href="#2-2-什么是-DI？" class="headerlink" title="2.2 什么是 DI？"></a>2.2 什么是 DI？</h3><p><strong>DI（Dependency Injection）依赖注入</strong>，是 IoC 的一种具体实现方式。容器在创建 Bean 时，自动将依赖的对象注入到目标 Bean 中。</p><p>我们可以把 DI 想象成<strong>医院的输液系统</strong>：</p><ul><li>💉 病人（目标对象）需要药物（依赖）</li><li>💉 不是病人自己去找药，而是护士（容器）把药接上</li><li>💉 药瓶空了？护士会自动换一个新的（容器管理对象生命周期）</li></ul><h3 id="2-3-Spring-IoC-容器详解"><a href="#2-3-Spring-IoC-容器详解" class="headerlink" title="2.3 Spring IoC 容器详解"></a>2.3 Spring IoC 容器详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🏭 Spring IoC 容器"] --> B["📦 BeanFactory\n（基础容器）"]    A --> C["📋 ApplicationContext\n（高级容器）"]        B --> B1["延迟加载\n轻量级"]        C --> C1["启动即加载\n事件机制\n资源国际化"]    C1 --> C2["ClassPathXmlApplicationContext"]    C1 --> C3["FileSystemXmlApplicationContext"]    C1 --> C4["AnnotationConfigApplicationContext"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd</pre></div><p><strong>Spring IoC 容器的核心接口：</strong></p><ol><li><p><strong>BeanFactory</strong>：最基础的 IoC 容器接口</p><ul><li>采用延迟加载策略，只有当调用 <code>getBean()</code> 时才创建 Bean</li><li>适合资源敏感的场景</li></ul></li><li><p><strong>ApplicationContext</strong>：功能更强大的高级容器</p><ul><li>启动时即加载所有 Bean</li><li>提供国际化（i18n）支持</li><li>提供事件发布机制</li><li>是我们在实际开发中最常用的接口</li></ul></li></ol><p><strong>ApplicationContext 的三个实现类：</strong></p><table><thead><tr><th>实现类</th><th>适用场景</th><th>配置文件位置</th></tr></thead><tbody><tr><td><strong>ClassPathXmlApplicationContext</strong></td><td>XML 配置方式</td><td>类路径下（src）</td></tr><tr><td><strong>FileSystemXmlApplicationContext</strong></td><td>XML 配置方式</td><td>文件系统路径</td></tr><tr><td><strong>AnnotationConfigApplicationContext</strong></td><td>注解配置方式</td><td>Java 代码中指定</td></tr></tbody></table><h3 id="2-4-三种依赖注入方式详解"><a href="#2-4-三种依赖注入方式详解" class="headerlink" title="2.4 三种依赖注入方式详解"></a>2.4 三种依赖注入方式详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["💉 依赖注入方式"] --> B["🔨 构造器注入"]    A --> C["📝 Setter 注入"]    A --> D["🔍 字段注入"]        B --> B1["✅ 推荐\n强制依赖\n不可变对象"]        C --> C1["⚠️ 可选依赖\nSetter 多次调用"]        D --> D2["❌ 不推荐\n难以测试\n隐蔽依赖"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#ffcdd2</pre></div><p><strong>方式一：构造器注入（推荐使用）</strong></p><p>构造器注入是 Spring 官方推荐的注入方式，特别适合处理<strong>必选依赖</strong>。</p><p><strong>优点：</strong></p><ul><li>✅ 依赖对象在构造时就完全初始化，不会出现空指针</li><li>✅ 构造器参数可以声明为 <code>final</code>，保证不可变性</li><li>✅ 通过构造器可以明确看出这个类需要哪些依赖</li><li>✅ 有利于单元测试，直接传 Mock 对象即可</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 构造器注入示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserDao userDao;      <span class="comment">// 声明为 final，保证不可变</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> EmailService emailService;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 使用 @Autowired 标记构造器，Spring 会自动注入参数</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">UserService</span><span class="params">(UserDao userDao, EmailService emailService)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userDao = userDao;</span><br><span class="line">        <span class="built_in">this</span>.emailService = emailService;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">register</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        userDao.save(user);</span><br><span class="line">        emailService.sendWelcomeEmail(user.getEmail());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>方式二：Setter 注入</strong></p><p>Setter 注入通过 setter 方法注入依赖，适合处理<strong>可选依赖</strong>。</p><p><strong>使用场景：</strong></p><ul><li>依赖可能在后续操作中才设置</li><li>需要在运行时更换依赖实现</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Setter 注入示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;  <span class="comment">// 非 final，可以后续修改</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> NotificationService notificationService;  <span class="comment">// 可选依赖</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUserDao</span><span class="params">(UserDao userDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userDao = userDao;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setNotificationService</span><span class="params">(NotificationService notificationService)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.notificationService = notificationService;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">notifyUser</span><span class="params">(Long userId, String message)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (notificationService != <span class="literal">null</span>) &#123;  <span class="comment">// 检查是否为 null</span></span><br><span class="line">            notificationService.send(userId, message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>方式三：字段注入（不推荐）</strong></p><p>字段注入直接在字段上加 <code>@Autowired</code>，看起来最简洁，但不推荐使用。</p><p><strong>为什么不推荐？</strong></p><ul><li>❌ 依赖隐蔽，构造器中看不到有哪些依赖</li><li>❌ 难以进行单元测试，无法手动传入 Mock 对象</li><li>❌ 容易导致空指针异常（忘记注入）</li><li>❌ 违反单一职责原则，类职责不清晰</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 字段注入（不推荐，但很常见）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span>  <span class="comment">// ❌ 不推荐</span></span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> EmailService emailService;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-5-Autowired-注解详解"><a href="#2-5-Autowired-注解详解" class="headerlink" title="2.5 @Autowired 注解详解"></a>2.5 @Autowired 注解详解</h3><p><code>@Autowired</code> 是 Spring 中最常用的依赖注入注解，它有几个关键用法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Autowired</span> 注解的作用域详解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景一：构造器注入（Spring 5.0+，单构造器时可省略 @Autowired）</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 只有一个构造器时，@Autowired 可以省略</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">UserService</span><span class="params">(UserDao userDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userDao = userDao;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景二：Setter 方法注入</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUserDao</span><span class="params">(UserDao userDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userDao = userDao;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景三：字段注入（不推荐）</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景四：当存在多个同类型 Bean 时，使用 @Qualifier 指定</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 指定使用 &quot;mysqlUserDao&quot; 这个 Bean</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="meta">@Qualifier(&quot;mysqlUserDao&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景五：使用 @Primary 指定默认实现</span></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="meta">@Primary</span>  <span class="comment">// 默认优先使用这个实现</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MysqlUserDao</span> <span class="keyword">implements</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    <span class="comment">// 这是默认实现</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OracleUserDao</span> <span class="keyword">implements</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    <span class="comment">// 如果没有明确指定，使用带 @Primary 的</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景六：@Resource 注解（Java 标准注解，不仅仅是 Spring）</span></span><br><span class="line"><span class="comment">// @Resource(name = &quot;mysqlUserDao&quot;)  // 等同于 @Qualifier</span></span><br></pre></td></tr></table></figure><hr><h2 id="三、Spring-Bean-管理详解"><a href="#三、Spring-Bean-管理详解" class="headerlink" title="三、Spring Bean 管理详解"></a>三、Spring Bean 管理详解</h2><h3 id="3-1-Bean-的作用域"><a href="#3-1-Bean-的作用域" class="headerlink" title="3.1 Bean 的作用域"></a>3.1 Bean 的作用域</h3><p>Spring 根据作用域将 Bean 分为以下几类：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📋 Bean 作用域"] --> B["singleton\n单例模式"]    A --> C["prototype\n原型模式"]    A --> D["request\n请求级别"]    A --> E["session\n会话级别"]        B --> B1["整个应用只有一个\n（默认）"]        C --> C1["每次获取\n创建新实例"]        D --> D1["同一 HTTP 请求\n共享实例"]        E --> E1["同一 HTTP 会话\n共享实例"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><p><strong>详解五种作用域：</strong></p><p><strong>1️⃣ singleton（单例模式）—— 默认作用域</strong></p><p>在 Spring IoC 容器中，每个 Bean 只会有<strong>一个共享实例</strong>。无论你获取多少次这个 Bean，得到的都是同一个对象。</p><p><strong>使用场景：</strong></p><ul><li>无状态的 Bean（不保存实例状态，如 Service、Repository）</li><li>性能优化，减少对象创建开销</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Scope(&quot;singleton&quot;)</span>  <span class="comment">// 可以省略，默认就是 singleton</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">requestCount</span> <span class="operator">=</span> <span class="number">0</span>;  <span class="comment">// ⚠️ 所有请求共享这个变量！</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addRequest</span><span class="params">()</span> &#123;</span><br><span class="line">        requestCount++;  <span class="comment">// 线程安全问题！</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>2️⃣ prototype（原型模式）</strong></p><p>每次从容器中获取 Bean 时，都会创建一个<strong>新的实例</strong>。</p><p><strong>使用场景：</strong></p><ul><li>有状态的 Bean，需要保持每次请求的独立性</li><li>对象创建成本较高，且不需要共享</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Scope(&quot;prototype&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;OrderItem&gt; items = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();  <span class="comment">// 每个实例独立</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addItem</span><span class="params">(OrderItem item)</span> &#123;</span><br><span class="line">        items.add(item);  <span class="comment">// 每个实例有自己独立的 items</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>3️⃣ request（请求级别）—— Web 应用专用</strong></p><p>同一个 HTTP 请求内，所有获取的 Bean 都是同一个实例；不同请求则创建不同实例。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Scope(&quot;request&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestScopeService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String requestId;  <span class="comment">// 每个请求有独立的 requestId</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.requestId = UUID.randomUUID().toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>4️⃣ session（会话级别）—— Web 应用专用</strong></p><p>同一用户会话内获取的 Bean 是同一个实例；不同用户会话则创建不同实例。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Scope(&quot;session&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserPreferencesService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> UserPreferences preferences;  <span class="comment">// 每个用户有独立的偏好设置</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setTheme</span><span class="params">(String theme)</span> &#123;</span><br><span class="line">        preferences.setTheme(theme);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>5️⃣ application（应用级别）</strong></p><p>整个 Web 应用只有一个实例，类似于单例但属于 ServletContext 范围。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Scope(&quot;application&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppCounterService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> totalUsers;  <span class="comment">// 整个应用的计数器</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">increment</span><span class="params">()</span> &#123;</span><br><span class="line">        totalUsers++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-Bean-的生命周期详解"><a href="#3-2-Bean-的生命周期详解" class="headerlink" title="3.2 Bean 的生命周期详解"></a>3.2 Bean 的生命周期详解</h3><p>理解 Bean 的生命周期，有助于我们在正确的时机执行初始化和清理逻辑：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔄 Bean 生命周期"] --> B["1️⃣ 实例化"]    B --> C["2️⃣ 属性填充\n（依赖注入）"]    C --> D["3️⃣ BeanNameAware"]    D --> E["4️⃣ BeanFactoryAware"]    E --> F["5️⃣ ApplicationContextAware"]    F --> G["6️⃣ BeanPostProcessor\n前置处理"]    G --> H["7️⃣ InitializingBean\nafterPropertiesSet"]    H --> I["8️⃣ 自定义 init-method"]    I --> J["9️⃣ BeanPostProcessor\n后置处理"]    J --> K["🔟 Bean 就绪状态"]    K --> L["🛑 销毁\nDisposableBean\ndestroy-method"]        style A fill:#fff3e0    style K fill:#c8e6c9    style L fill:#f8bbd0</pre></div><p><strong>每个阶段详解：</strong></p><table><thead><tr><th>阶段</th><th>说明</th><th>可实现接口&#x2F;注解</th></tr></thead><tbody><tr><td><strong>1. 实例化</strong></td><td>调用构造器创建 Bean 实例</td><td>无</td></tr><tr><td><strong>2. 属性填充</strong></td><td>Spring 注入配置的属性和依赖</td><td>@Autowired, @Value</td></tr><tr><td><strong>3. BeanNameAware</strong></td><td>将 Bean 的名称注入到 Bean 中</td><td>BeanNameAware</td></tr><tr><td><strong>4. BeanFactoryAware</strong></td><td>将 BeanFactory 注入到 Bean 中</td><td>BeanFactoryAware</td></tr><tr><td><strong>5. ApplicationContextAware</strong></td><td>将 ApplicationContext 注入</td><td>ApplicationContextAware</td></tr><tr><td><strong>6. BeanPostProcessor 前置</strong></td><td>对 Bean 进行预处理</td><td>BeanPostProcessor</td></tr><tr><td><strong>7. InitializingBean</strong></td><td>初始化逻辑</td><td>InitializingBean.afterPropertiesSet()</td></tr><tr><td><strong>8. 自定义 init-method</strong></td><td>自定义初始化方法</td><td>@PostConstruct 或 @Bean(initMethod)</td></tr><tr><td><strong>9. BeanPostProcessor 后置</strong></td><td>对 Bean 进行后处理</td><td>BeanPostProcessor</td></tr><tr><td><strong>10. Bean 就绪</strong></td><td>Bean 可以正常使用了</td><td>-</td></tr><tr><td><strong>11. 销毁</strong></td><td>清理资源，关闭连接</td><td>DisposableBean.destroy()</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 完整的 Bean 生命周期演示</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span>, DisposableBean &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> UserDao userDao;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 1. 构造器（阶段一）</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">UserService</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;1️⃣ UserService 构造器被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 2. 属性注入（阶段二）</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;2️⃣ 属性注入：&quot;</span> + name);</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 3. BeanNameAware（阶段三）</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBeanName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;3️⃣ Bean 名称：&quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 4. 初始化方法（阶段七/八）</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;7️⃣ InitializingBean.afterPropertiesSet() 被调用&quot;</span>);</span><br><span class="line">        <span class="comment">// 在这里可以执行初始化逻辑，如校验参数、建立连接等</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 5. 自定义初始化方法（阶段八）</span></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;8️⃣ @PostConstruct 初始化方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 6. 销毁方法（阶段十一）</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">destroy</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;🛑 DisposableBean.destroy() 被调用&quot;</span>);</span><br><span class="line">        <span class="comment">// 在这里可以执行清理逻辑，如关闭连接、释放资源等</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 7. 自定义销毁方法（阶段十一）</span></span><br><span class="line">    <span class="meta">@PreDestroy</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cleanup</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;🗑️ @PreDestroy 销毁方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-3-组件扫描与注册"><a href="#3-3-组件扫描与注册" class="headerlink" title="3.3 组件扫描与注册"></a>3.3 组件扫描与注册</h3><p>Spring 通过<strong>组件扫描</strong>（Component Scanning）自动发现和注册带有 <code>@Component</code> 及其衍生注解（如 <code>@Service</code>、<code>@Repository</code>、<code>@Controller</code>）的类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 组件扫描配置详解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式一：@ComponentScan 基本用法</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(basePackages = &quot;com.example&quot;)</span>  <span class="comment">// 扫描 com.example 包及其子包</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二：排除特定组件</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(</span></span><br><span class="line"><span class="meta">    basePackages = &quot;com.example&quot;,</span></span><br><span class="line"><span class="meta">    excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = &quot;.*Dao&quot;)</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式三：只包含特定组件</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(</span></span><br><span class="line"><span class="meta">    basePackages = &quot;com.example&quot;,</span></span><br><span class="line"><span class="meta">    includeFilters = @ComponentScan.Filter(</span></span><br><span class="line"><span class="meta">        type = FilterType.ANNOTATION,</span></span><br><span class="line"><span class="meta">        classes = &#123;Controller.class, Service.class&#125;</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式四：排除特定类型的组件</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(</span></span><br><span class="line"><span class="meta">    basePackages = &quot;com.example&quot;,</span></span><br><span class="line"><span class="meta">    excludeFilters = @ComponentScan.Filter(</span></span><br><span class="line"><span class="meta">        type = FilterType.ANNOTATION,</span></span><br><span class="line"><span class="meta">        classes = Controller.class  // 排除所有 Controller</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-Conditional-条件注册"><a href="#3-4-Conditional-条件注册" class="headerlink" title="3.4 @Conditional 条件注册"></a>3.4 @Conditional 条件注册</h3><p><code>@Conditional</code> 注解允许我们根据特定条件决定是否注册某个 Bean，这在多环境配置和功能开关场景中非常有用。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 条件注册 Bean 示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景一：根据类是否存在条件注册</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(name = &quot;com.example.thirdparty.ThirdPartyService&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> ThirdPartyService <span class="title function_">thirdPartyService</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ThirdPartyService</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景二：根据配置文件条件注册</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ConditionalOnProperty(name = &quot;feature.enabled&quot;, havingValue = &quot;true&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FeatureConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> FeatureService <span class="title function_">featureService</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">FeatureService</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景三：根据环境条件注册</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@Profile(&quot;prod&quot;)</span>  <span class="comment">// 只在生产环境生效</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProdConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> AlertService <span class="title function_">alertService</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">EmailAlertService</span>();  <span class="comment">// 生产环境发邮件告警</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@Profile(&quot;dev&quot;)</span>  <span class="comment">// 只在开发环境生效</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DevConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> AlertService <span class="title function_">alertService</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ConsoleAlertService</span>();  <span class="comment">// 开发环境打印到控制台</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、Spring-AOP-面向切面编程"><a href="#四、Spring-AOP-面向切面编程" class="headerlink" title="四、Spring AOP 面向切面编程"></a>四、Spring AOP 面向切面编程</h2><h3 id="4-1-为什么需要-AOP？"><a href="#4-1-为什么需要-AOP？" class="headerlink" title="4.1 为什么需要 AOP？"></a>4.1 为什么需要 AOP？</h3><p>在软件开发中，有一些<strong>横切关注点</strong>（Cross-cutting Concerns）会分散在多个模块中，例如：</p><ul><li>📝 <strong>日志记录</strong>：在每个方法开始和结束时记录日志</li><li>🔒 <strong>安全控制</strong>：在方法执行前检查用户权限</li><li>🔄 <strong>事务管理</strong>：在方法开始时开启事务，结束时提交&#x2F;回滚</li><li>📊 <strong>性能监控</strong>：记录每个方法的执行时间</li></ul><p>这些功能如果每个方法都手动编写，会导致：</p><ol><li>代码重复：同样的逻辑在多个地方重复</li><li>业务逻辑不清晰：核心业务被横切逻辑污染</li><li>修改成本高：横切逻辑改变需要修改所有模块</li></ol><p><strong>AOP 的解决方案是</strong>：将这些横切关注点封装成独立的模块（切面），然后声明式地将它们”织入”到需要的方法中。</p><h3 id="4-2-AOP-核心概念详解"><a href="#4-2-AOP-核心概念详解" class="headerlink" title="4.2 AOP 核心概念详解"></a>4.2 AOP 核心概念详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🧩 AOP 核心概念"] --> B["🎯 Aspect\n切面"]    A --> C["📍 Pointcut\n切入点"]    A --> D["➡️ Join Point\n连接点"]    A --> E["🔔 Advice\n通知"]    A --> F["🔄 Weaving\n织入"]        B --> B1["封装横切逻辑\n的模块"]        C --> C1["定义哪些\nJoin Point\n需要拦截"]        D --> D1["可被拦截\n的方法调用"]        E --> E1["拦截后\n执行的逻辑"]    E1 --> E2["前置通知"]    E1 --> E3["后置通知"]    E1 --> E4["返回通知"]    E1 --> E5["异常通知"]    E1 --> E6["环绕通知"]        style A fill:#fff3e0</pre></div><p><strong>核心概念详解：</strong></p><p><strong>🎯 Aspect（切面）：</strong><br>切面是<strong>横切关注点的模块化封装</strong>。你可以把切面想象成一个”插件”，它定义了在哪些地方（切入点）做什么（通知）。</p><p><strong>📍 Pointcut（切入点）：</strong><br>切入点用于<strong>定义哪些连接点需要被拦截</strong>。你可以理解为”在哪些方法上动手脚”的规则。</p><p><strong>➡️ Join Point（连接点）：</strong><br>连接点是<strong>程序执行的某个位置</strong>，可以被切面拦截。在 Spring AOP 中，连接点特指<strong>方法调用</strong>。</p><p><strong>🔔 Advice（通知&#x2F;增强）：</strong><br>通知是<strong>拦截到连接点后执行的逻辑</strong>，分为五种类型：</p><ul><li><code>@Before</code>：前置通知，在方法执行前执行</li><li><code>@After</code>：后置通知，在方法执行后执行（无论成功或失败）</li><li><code>@AfterReturning</code>：返回通知，在方法成功返回后执行</li><li><code>@AfterThrowing</code>：异常通知，在方法抛出异常时执行</li><li><code>@Around</code>：环绕通知，可以控制方法是否执行以及执行时机</li></ul><p><strong>🔄 Weaving（织入）：</strong><br>织入是把切面应用到目标对象的过程。Spring AOP 默认采用<strong>动态代理</strong>方式进行织入。</p><h3 id="4-3-AOP-工作流程图解"><a href="#4-3-AOP-工作流程图解" class="headerlink" title="4.3 AOP 工作流程图解"></a>4.3 AOP 工作流程图解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📋 AOP 工作流程"] --> B["客户端请求"]    B --> C["目标方法调用"]    C --> D{"切点匹配？"}    D -->|"是"| E["前置通知\n@Before"]    E --> F["目标方法执行"]    F --> G["后置通知\n@AfterReturning"]    G --> H["返回客户端"]    D -->|"否"| H    F -.->|"异常时"| I["异常通知\n@AfterThrowing"]    I --> H        style E fill:#c8e6c9    style G fill:#c8e6c9    style I fill:#ffcdd2</pre></div><h3 id="4-4-AOP-示例代码详解"><a href="#4-4-AOP-示例代码详解" class="headerlink" title="4.4 AOP 示例代码详解"></a>4.4 AOP 示例代码详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义切面类</span></span><br><span class="line"><span class="comment"> * 切面 = 切入点表达式 + 通知</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Aspect</span>     <span class="comment">// 标注为切面类（注意：需要同时标注 @Component 才能被扫描）</span></span><br><span class="line"><span class="meta">@Component</span>  <span class="comment">// 加入 Spring 容器管理</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoggingAspect</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 切入点表达式定义</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 切入点：拦截 com.example.service 包下所有类的所有方法</span></span><br><span class="line">    <span class="comment">// 语法：execution([返回值类型] [包名.类名.方法名](参数))</span></span><br><span class="line">    <span class="comment">// * 表示任意返回值类型</span></span><br><span class="line">    <span class="comment">// *.* 表示任意包下的任意类</span></span><br><span class="line">    <span class="comment">// *(..) 表示任意方法，任意参数</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.example.service.*.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">servicePointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 切入点：拦截标注 @Loggable 注解的方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;@annotation(com.example.annotation.Loggable)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">loggablePointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 通知方法定义</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 前置通知：在目标方法执行前执行</span></span><br><span class="line">    <span class="meta">@Before(&quot;servicePointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">before</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        <span class="comment">// JoinPoint 封装了被拦截方法的信息</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().toShortString();  <span class="comment">// 方法签名</span></span><br><span class="line">        Object[] args = joinPoint.getArgs();  <span class="comment">// 方法参数</span></span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;🔔 [前置通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 即将执行&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;    参数：&quot;</span> + Arrays.toString(args));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 后置通知：在目标方法执行后执行（无论成功或失败）</span></span><br><span class="line">    <span class="meta">@After(&quot;servicePointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">after</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().toShortString();</span><br><span class="line">        System.out.println(<span class="string">&quot;🔔 [后置通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 执行完成&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 返回通知：在目标方法成功返回后执行</span></span><br><span class="line">    <span class="meta">@AfterReturning(pointcut = &quot;servicePointcut()&quot;, returning = &quot;result&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterReturning</span><span class="params">(JoinPoint joinPoint, Object result)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().toShortString();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ [返回通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 返回结果：&quot;</span> + result);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 异常通知：在目标方法抛出异常时执行</span></span><br><span class="line">    <span class="meta">@AfterThrowing(pointcut = &quot;servicePointcut()&quot;, throwing = &quot;exception&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterThrowing</span><span class="params">(JoinPoint joinPoint, Throwable exception)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().toShortString();</span><br><span class="line">        System.out.println(<span class="string">&quot;❌ [异常通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 抛出异常：&quot;</span> + exception.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 环绕通知：最强大的通知类型</span></span><br><span class="line">    <span class="comment">// 可以在方法执行前后执行自定义逻辑，甚至决定是否执行方法</span></span><br><span class="line">    <span class="meta">@Around(&quot;servicePointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">around</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().toShortString();</span><br><span class="line">        <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;⏰ [环绕通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 开始执行&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// proceed() 方法执行目标方法</span></span><br><span class="line">            <span class="comment">// 如果不调用 proceed()，目标方法不会执行</span></span><br><span class="line">            <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> joinPoint.proceed();</span><br><span class="line">            </span><br><span class="line">            <span class="type">long</span> <span class="variable">duration</span> <span class="operator">=</span> System.currentTimeMillis() - startTime;</span><br><span class="line">            System.out.println(<span class="string">&quot;⏱️ [环绕通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 执行成功，耗时：&quot;</span> + duration + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">duration</span> <span class="operator">=</span> System.currentTimeMillis() - startTime;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ [环绕通知] 方法 &quot;</span> + methodName + <span class="string">&quot; 执行失败，耗时：&quot;</span> + duration + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">            <span class="keyword">throw</span> e;  <span class="comment">// 重新抛出异常，让调用者处理</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-5-常用切入点表达式详解"><a href="#4-5-常用切入点表达式详解" class="headerlink" title="4.5 常用切入点表达式详解"></a>4.5 常用切入点表达式详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 切入点表达式语法详解</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * execution([修饰符] 返回值类型 [包名.类名.方法名](参数))</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * 特殊符号：</span></span><br><span class="line"><span class="comment"> * - * ：匹配任意字符</span></span><br><span class="line"><span class="comment"> * - .. ：匹配任意参数或任意层级包</span></span><br><span class="line"><span class="comment"> * - +  ：匹配子类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PointcutExpressions</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例一：拦截特定类的方法</span></span><br><span class="line">    <span class="comment">// 拦截 UserService 类的所有方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.example.service.UserService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">userServicePointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例二：拦截特定方法（精确到方法名和参数类型）</span></span><br><span class="line">    <span class="comment">// 拦截 findById(Long) 方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.example.service.UserService.findById(Long))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">findByIdPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例三：拦截返回值为特定类型的方法</span></span><br><span class="line">    <span class="comment">// 返回 User 类型的所有方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(com.example.entity.User com.example.service.*.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">userReturnPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例四：拦截特定包下所有子包的方法</span></span><br><span class="line">    <span class="comment">// com.example.service.. 表示 service 包及其所有子包</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.example.service..*.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">servicePackagePointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例五：拦截所有 public 方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(public * *(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">publicMethodPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例六：拦截所有带 @Transactional 注解的方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;@annotation(org.springframework.transaction.annotation.Transactional)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transactionalPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例七：组合切入点（使用 &amp;&amp; || !）</span></span><br><span class="line">    <span class="comment">// 拦截 service 包的方法，但排除 save 开头的方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.example.service..*.*(..)) &amp;&amp; !execution(* com.example.service..save*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">serviceExceptSavePointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例八：within —— 拦截特定类/包的所有方法</span></span><br><span class="line">    <span class="comment">// 拦截 UserService 类中的所有方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;within(com.example.service.UserService)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">withinPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例九：this —— 拦截代理对象是特定类型的方法</span></span><br><span class="line">    <span class="comment">// 当代理对象是 UserService 类型时拦截</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;this(com.example.service.UserService)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">thisPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 示例十：args —— 拦截参数类型匹配的方法</span></span><br><span class="line">    <span class="comment">// 拦截参数第一个是 Long 类型的方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.example.service.UserService.findById(Long))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">argsPointcut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-6-自定义注解-AOP-实战"><a href="#4-6-自定义注解-AOP-实战" class="headerlink" title="4.6 自定义注解 + AOP 实战"></a>4.6 自定义注解 + AOP 实战</h3><p>通过结合自定义注解和 AOP，我们可以实现功能更明确、使用更便捷的切面。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 第一步：定义注解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Target(ElementType.METHOD)</span>  <span class="comment">// 注解可以标注在方法上</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span>  <span class="comment">// 注解在运行时可见</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Loggable &#123;</span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;           <span class="comment">// 注解属性：日志描述</span></span><br><span class="line">    LogLevel <span class="title function_">level</span><span class="params">()</span> <span class="keyword">default</span> LogLevel.INFO;  <span class="comment">// 注解属性：日志级别</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">LogLevel</span> &#123;</span><br><span class="line">    DEBUG, INFO, WARN, ERROR</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 第二步：定义切面处理注解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoggableAspect</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 拦截所有标注 @Loggable 注解的方法</span></span><br><span class="line">    <span class="meta">@Around(&quot;@annotation(Loggable)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">around</span><span class="params">(ProceedingJoinPoint joinPoint, </span></span><br><span class="line"><span class="params">                        Loggable loggable)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> joinPoint.getSignature().toShortString();</span><br><span class="line">        <span class="type">String</span> <span class="variable">annotationValue</span> <span class="operator">=</span> loggable.value();</span><br><span class="line">        <span class="type">LogLevel</span> <span class="variable">level</span> <span class="operator">=</span> loggable.level();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 根据日志级别输出日志</span></span><br><span class="line">        log(level, <span class="string">&quot;📝 [&quot;</span> + level + <span class="string">&quot;] 方法 &quot;</span> + methodName + <span class="string">&quot; 注解值：&quot;</span> + annotationValue);</span><br><span class="line">        </span><br><span class="line">        <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> joinPoint.proceed();</span><br><span class="line">            <span class="type">long</span> <span class="variable">duration</span> <span class="operator">=</span> System.currentTimeMillis() - startTime;</span><br><span class="line">            log(level, <span class="string">&quot;✅ 方法执行成功，耗时：&quot;</span> + duration + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">duration</span> <span class="operator">=</span> System.currentTimeMillis() - startTime;</span><br><span class="line">            log(LogLevel.ERROR, <span class="string">&quot;❌ 方法执行异常：&quot;</span> + e.getMessage());</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">log</span><span class="params">(LogLevel level, String message)</span> &#123;</span><br><span class="line">        <span class="keyword">switch</span> (level) &#123;</span><br><span class="line">            <span class="keyword">case</span> DEBUG: System.out.println(<span class="string">&quot;[DEBUG] &quot;</span> + message); <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> INFO: System.out.println(<span class="string">&quot;[INFO] &quot;</span> + message); <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> WARN: System.out.println(<span class="string">&quot;[WARN] &quot;</span> + message); <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> ERROR: System.err.println(<span class="string">&quot;[ERROR] &quot;</span> + message); <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 第三步：在需要记录日志的方法上使用注解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Loggable(value = &quot;查询用户详情&quot;, level = LogLevel.INFO)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">findById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userDao.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Loggable(value = &quot;保存用户&quot;, level = LogLevel.WARN)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">save</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userDao.save(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、Spring-事务管理"><a href="#五、Spring-事务管理" class="headerlink" title="五、Spring 事务管理"></a>五、Spring 事务管理</h2><h3 id="5-1-为什么需要事务？"><a href="#5-1-为什么需要事务？" class="headerlink" title="5.1 为什么需要事务？"></a>5.1 为什么需要事务？</h3><p>事务是<strong>一组操作，要么全部成功，要么全部失败</strong>的机制。典型场景是银行转账：</p><p>假设 A 账户给 B 账户转账 1000 元，操作包括：</p><ol><li>从 A 账户扣除 1000 元</li><li>向 B 账户增加 1000 元</li></ol><p>如果第一步成功但第二步失败（比如数据库崩溃），A 账户的钱就莫名其妙少了！这就需要事务来保证：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔒 事务 ACID 特性"] --> B["A - Atomicity\n原子性"]    A --> C["C - Consistency\n一致性"]    A --> D["I - Isolation\n隔离性"]    A --> E["D - Durability\n持久性"]        B --> B1["全部成功\n或全部失败"]        C --> C1["事务前后\n数据合法一致"]        D --> D1["并发控制\n隔离级别"]        E --> E1["提交后\n永久保存"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#f8bbd0</pre></div><p><strong>ACID 特性详解：</strong></p><p><strong>A - Atomicity（原子性）：</strong><br>事务中的所有操作要么全部完成，要么全部不执行。如果事务中途失败，已经执行的操作会被撤销。</p><p><strong>C - Consistency（一致性）：</strong><br>事务执行前后，数据库的状态保持一致。例如转账前后，总金额不变。</p><p><strong>I - Isolation（隔离性）：</strong><br>并发执行的事务之间互不干扰。数据库通过隔离级别来控制并发程度。</p><p><strong>D - Durability（持久性）：</strong><br>事务一旦提交，其对数据库的修改就是永久性的，即使系统崩溃也不会丢失。</p><h3 id="5-2-Spring-事务管理方式"><a href="#5-2-Spring-事务管理方式" class="headerlink" title="5.2 Spring 事务管理方式"></a>5.2 Spring 事务管理方式</h3><p>Spring 提供两种事务管理方式：</p><p><strong>方式一：编程式事务（了解即可，不推荐使用）</strong></p><p>通过编写代码手动管理事务，可以精确控制，但代码侵入性高。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 编程式事务管理示例</span></span><br><span class="line"><span class="comment"> * 这种方式代码侵入性高，不推荐在日常开发中使用</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> PlatformTransactionManager transactionManager;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Long fromId, Long toId, BigDecimal amount)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 定义事务属性</span></span><br><span class="line">        <span class="type">DefaultTransactionDefinition</span> <span class="variable">definition</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultTransactionDefinition</span>();</span><br><span class="line">        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);</span><br><span class="line">        definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 获取事务状态</span></span><br><span class="line">        <span class="type">TransactionStatus</span> <span class="variable">status</span> <span class="operator">=</span> transactionManager.getTransaction(definition);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 3. 执行业务操作</span></span><br><span class="line">            accountDao.deduct(fromId, amount);</span><br><span class="line">            accountDao.add(toId, amount);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 4. 提交事务</span></span><br><span class="line">            transactionManager.commit(status);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">// 5. 发生异常，回滚事务</span></span><br><span class="line">            transactionManager.rollback(status);</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>方式二：声明式事务（推荐使用）</strong></p><p>通过 <code>@Transactional</code> 注解声明式地管理事务，代码侵入性低，是实际开发中的主要方式。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 声明式事务管理示例</span></span><br><span class="line"><span class="comment"> * 只需在方法或类上加 <span class="doctag">@Transactional</span> 注解即可</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 在方法上加 @Transactional，方法内的所有操作都在一个事务中</span></span><br><span class="line">    <span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Long fromId, Long toId, BigDecimal amount)</span> &#123;</span><br><span class="line">        accountDao.deduct(fromId, amount);</span><br><span class="line">        accountDao.add(toId, amount);</span><br><span class="line">        <span class="comment">// 如果发生任何异常，Spring 会自动回滚事务</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 在类上加 @Transactional，类中所有方法都会在事务中执行</span></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">saveUser</span><span class="params">(User user)</span> &#123; <span class="comment">/* ... */</span> &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateUser</span><span class="params">(User user)</span> &#123; <span class="comment">/* ... */</span> &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUser</span><span class="params">(Long id)</span> &#123; <span class="comment">/* ... */</span> &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-3-Transactional-注解详解"><a href="#5-3-Transactional-注解详解" class="headerlink" title="5.3 @Transactional 注解详解"></a>5.3 @Transactional 注解详解</h3><p><code>@Transactional</code> 注解有多个参数，每个参数控制事务的不同方面：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Transactional</span> 注解参数详解</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 1. rollbackFor：指定触发回滚的异常类型</span></span><br><span class="line">    <span class="comment">// 默认只对 RuntimeException 和 Error 回滚</span></span><br><span class="line">    <span class="comment">// 如果想让所有异常都回滚，指定为 Exception.class</span></span><br><span class="line">    <span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 任何异常都会回滚，包括受检异常</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 2. noRollbackFor：指定不触发回滚的异常类型</span></span><br><span class="line">    <span class="comment">// 业务异常通常不希望回滚，而是返回错误码</span></span><br><span class="line">    <span class="meta">@Transactional(noRollbackFor = BusinessException.class)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// BusinessException 不会触发回滚</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 3. propagation：事务传播行为</span></span><br><span class="line">    <span class="comment">// 控制当一个事务方法被另一个事务方法调用时，事务如何传播</span></span><br><span class="line">    <span class="meta">@Transactional(propagation = Propagation.REQUIRED)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method3</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * REQUIRED：加入已存在的事务，或创建新事务（默认）</span></span><br><span class="line"><span class="comment">         * REQUIRES_NEW：挂起当前事务，创建新事务</span></span><br><span class="line"><span class="comment">         * NESTED：如果当前有事务则嵌套执行，否则创建新事务</span></span><br><span class="line"><span class="comment">         * SUPPORTS：如果当前有事务则加入，否则非事务执行</span></span><br><span class="line"><span class="comment">         * MANDATORY：必须在事务中执行，否则抛异常</span></span><br><span class="line"><span class="comment">         * NEVER：必须在非事务中执行，否则抛异常</span></span><br><span class="line"><span class="comment">         * NOT_SUPPORTED：挂起当前事务，非事务执行</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 4. isolation：事务隔离级别</span></span><br><span class="line">    <span class="comment">// 控制并发事务之间的相互影响程度</span></span><br><span class="line">    <span class="meta">@Transactional(isolation = Isolation.REPEATABLE_READ)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method4</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * READ_UNCOMMITTED：最低隔离级别，可能产生脏读</span></span><br><span class="line"><span class="comment">         * READ_COMMITTED：防止脏读，但可能产生不可重复读</span></span><br><span class="line"><span class="comment">         * REPEATABLE_READ：防止脏读和不可重复读，但可能产生幻读</span></span><br><span class="line"><span class="comment">         * SERIALIZABLE：最高隔离级别，完全串行执行</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 5. timeout：超时时间（秒）</span></span><br><span class="line">    <span class="comment">// 如果事务执行超过指定时间，自动回滚</span></span><br><span class="line">    <span class="meta">@Transactional(timeout = 30)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method5</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 超过 30 秒自动回滚</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 6. readOnly：只读事务</span></span><br><span class="line">    <span class="comment">// 提示数据库优化，适合只有查询的操作</span></span><br><span class="line">    <span class="meta">@Transactional(readOnly = true)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method6</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 只读事务，数据库可能会进行优化</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-4-事务传播行为详解"><a href="#5-4-事务传播行为详解" class="headerlink" title="5.4 事务传播行为详解"></a>5.4 事务传播行为详解</h3><p>事务传播行为定义了<strong>当一个事务方法被另一个事务方法调用时，事务如何处理</strong>。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🔄 事务传播行为"] --> B["REQUIRED\n（默认）"]    A --> C["REQUIRES_NEW"]    A --> D["NESTED"]    A --> E["SUPPORTS"]        B --> B1["加入已有事务\n或创建新事务"]        C --> C1["挂起当前事务\n创建全新事务"]        D --> D1["嵌套执行\n使用保存点"]        E --> E1["有则加入\n无则非事务"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><p><strong>常见传播行为详解：</strong></p><table><thead><tr><th>传播行为</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>REQUIRED</strong></td><td>如果当前有事务，加入；否则创建新事务</td><td>大多数业务操作</td></tr><tr><td><strong>REQUIRES_NEW</strong></td><td>挂起当前事务，创建全新事务</td><td>独立记录日志</td></tr><tr><td><strong>NESTED</strong></td><td>嵌套事务（使用保存点）</td><td>部分操作需要独立回滚</td></tr><tr><td><strong>SUPPORTS</strong></td><td>如果当前有事务加入；否则非事务执行</td><td>查询为主的操作</td></tr><tr><td><strong>MANDATORY</strong></td><td>必须在事务中执行</td><td>强制要求事务的方法</td></tr><tr><td><strong>NOT_SUPPORTED</strong></td><td>挂起事务，非事务执行</td><td>不需要事务的操作</td></tr></tbody></table><h3 id="5-5-Spring-事务原理"><a href="#5-5-Spring-事务原理" class="headerlink" title="5.5 Spring 事务原理"></a>5.5 Spring 事务原理</h3><p>Spring 事务的原理基于 <strong>AOP 动态代理</strong>。当你使用 <code>@Transactional</code> 注解时：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔄 Spring 事务原理"] --> B["1️⃣ Spring 为目标类\n创建代理对象"]    B --> C["2️⃣ 代理对象在调用\n目标方法前\n检查 @Transactional"]    C --> D["3️⃣ 如果有注解\n开启事务\n获取数据库连接"]    D --> E["4️⃣ 执行目标方法"]    E --> F{"执行结果？"}    F -->|"正常"| G["提交事务"]    F -->|"异常"| H["回滚事务"]    G --> I["返回结果"]    H --> I        style A fill:#fff3e0    style C fill:#c8e6c9    style G fill:#e3f2fd    style H fill:#ffcdd2</pre></div><p><strong>⚠️ 重要注意事项：@Transactional 在内部方法调用时不生效</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ❌ 错误示例：内部方法调用，事务不生效</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">outerMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        innerMethod();  <span class="comment">// ❌ 这里调用的是 this.innerMethod()</span></span><br><span class="line">        <span class="comment">// 因为 this 不是代理对象，所以事务不生效</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">innerMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 这里的代码实际上没有事务！</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ✅ 正确解决方案：注入自身代理对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 注入 Spring 容器中的代理对象（self 实际上是代理对象）</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService self;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">outerMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        self.innerMethod();  <span class="comment">// ✅ 通过代理对象调用，事务生效</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">innerMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 现在有事务了！</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、Spring-MVC-开发-Web-应用"><a href="#六、Spring-MVC-开发-Web-应用" class="headerlink" title="六、Spring MVC 开发 Web 应用"></a>六、Spring MVC 开发 Web 应用</h2><h3 id="6-1-Spring-MVC-概述"><a href="#6-1-Spring-MVC-概述" class="headerlink" title="6.1 Spring MVC 概述"></a>6.1 Spring MVC 概述</h3><p>Spring MVC 是 Spring 框架的 Web 模块，专门用于开发 Web 应用。它遵循 <strong>MVC（Model-View-Controller）</strong> 设计模式，将数据处理、页面展示、业务控制分离。</p><p><strong>MVC 职责分工：</strong></p><table><thead><tr><th>角色</th><th>职责</th><th>Spring MVC 对应</th></tr></thead><tbody><tr><td><strong>Model（模型）</strong></td><td>业务数据处理</td><td>Service、Repository、Entity</td></tr><tr><td><strong>View（视图）</strong></td><td>页面展示</td><td>JSP、Thymeleaf、JSON</td></tr><tr><td><strong>Controller（控制器）</strong></td><td>接收请求、调用服务、返回响应</td><td>@Controller、@RestController</td></tr></tbody></table><h3 id="6-2-Spring-MVC-工作流程"><a href="#6-2-Spring-MVC-工作流程" class="headerlink" title="6.2 Spring MVC 工作流程"></a>6.2 Spring MVC 工作流程</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔄 Spring MVC 工作流程"] --> B["1️⃣ 请求到达\nDispatchServlet"]    B --> C["2️⃣ 处理器映射\nHandlerMapping\n根据 URL 找到\n对应的 Controller"]    C --> D["3️⃣ 处理器适配器\nHandlerAdapter\n执行 Controller\n的方法"]    D --> E["4️⃣ Controller\n执行业务逻辑\n返回 ModelAndView"]    E --> F["5️⃣ 视图解析器\nViewResolver\n根据视图名\n找到对应视图"]    F --> G["6️⃣ 视图渲染\nView\n将 Model 数据\n填充到视图模板"]    G --> H["7️⃣ 返回响应\n给浏览器"]        style A fill:#fff3e0    style E fill:#c8e6c9</pre></div><p><strong>流程详解：</strong></p><ol><li><strong>DispatchServlet 接收请求</strong>：作为前端控制器，所有请求都先到达这里</li><li><strong>HandlerMapping 查找 Controller</strong>：根据 URL 找到对应的处理方法</li><li><strong>HandlerAdapter 执行 Controller</strong>：调用具体的处理方法</li><li><strong>Controller 返回 ModelAndView</strong>：包含模型数据和视图名</li><li><strong>ViewResolver 解析视图</strong>：根据视图名找到实际的视图文件</li><li><strong>View 渲染视图</strong>：将数据填充到模板中</li><li><strong>返回响应</strong>：生成 HTML 或 JSON 返回给浏览器</li></ol><h3 id="6-3-Controller-vs-RestController"><a href="#6-3-Controller-vs-RestController" class="headerlink" title="6.3 @Controller vs @RestController"></a>6.3 @Controller vs @RestController</h3><p>这两个注解用于不同的场景：</p><p><strong>@Controller：返回视图（JSP、Thymeleaf 等模板）</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 返回视图页面</span></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/list&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">list</span><span class="params">(Model model)</span> &#123;</span><br><span class="line">        <span class="comment">// 查询数据</span></span><br><span class="line">        List&lt;User&gt; users = userService.findAll();</span><br><span class="line">        <span class="comment">// 将数据存入模型</span></span><br><span class="line">        model.addAttribute(<span class="string">&quot;users&quot;</span>, users);</span><br><span class="line">        <span class="comment">// 返回视图名（Spring Boot 会自动拼接模板路径）</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;user/list&quot;</span>;  <span class="comment">// -&gt; templates/user/list.html</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 返回视图页面（REST 风格）</span></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/detail/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">detail</span><span class="params">(<span class="meta">@PathVariable</span> Long id, Model model)</span> &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userService.findById(id);</span><br><span class="line">        model.addAttribute(<span class="string">&quot;user&quot;</span>, user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;user/detail&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>@RestController：返回 JSON 数据（前后端分离）</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span>  <span class="comment">// = @Controller + @ResponseBody</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/api/users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserApiController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService userService;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// GET /api/users</span></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">list</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userService.findAll();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// GET /api/users/1</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userService.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// POST /api/users</span></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;User&gt; <span class="title function_">create</span><span class="params">(<span class="meta">@RequestBody</span> <span class="meta">@Valid</span> User user)</span> &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">saved</span> <span class="operator">=</span> userService.save(user);</span><br><span class="line">        <span class="keyword">return</span> Result.success(saved);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// PUT /api/users/1</span></span><br><span class="line">    <span class="meta">@PutMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@PathVariable</span> Long id, </span></span><br><span class="line"><span class="params">                              <span class="meta">@RequestBody</span> <span class="meta">@Valid</span> User user)</span> &#123;</span><br><span class="line">        user.setId(id);</span><br><span class="line">        userService.update(user);</span><br><span class="line">        <span class="keyword">return</span> Result.success();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// DELETE /api/users/1</span></span><br><span class="line">    <span class="meta">@DeleteMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">        userService.deleteById(id);</span><br><span class="line">        <span class="keyword">return</span> Result.success();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-4-请求参数绑定详解"><a href="#6-4-请求参数绑定详解" class="headerlink" title="6.4 请求参数绑定详解"></a>6.4 请求参数绑定详解</h3><p>Spring MVC 提供了丰富的参数绑定方式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 请求参数绑定示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/api/products&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProductController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 1. 路径变量 @PathVariable</span></span><br><span class="line">    <span class="comment">// 用于绑定 URL 路径中的变量</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/&#123;category&#125;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Product <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Long id,</span></span><br><span class="line"><span class="params">                          <span class="meta">@PathVariable</span> String category)</span> &#123;</span><br><span class="line">        <span class="comment">// GET /api/products/electronics/100</span></span><br><span class="line">        <span class="comment">// category = &quot;electronics&quot;, id = 100</span></span><br><span class="line">        <span class="keyword">return</span> productService.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 2. 查询参数 @RequestParam</span></span><br><span class="line">    <span class="comment">// 用于绑定 URL ?key=value 形式的参数</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> PageResult&lt;Product&gt; <span class="title function_">list</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@RequestParam(defaultValue = &quot;1&quot;)</span> <span class="type">int</span> page,      // 默认值</span></span><br><span class="line"><span class="params">            <span class="meta">@RequestParam(defaultValue = &quot;10&quot;)</span> <span class="type">int</span> size,</span></span><br><span class="line"><span class="params">            <span class="meta">@RequestParam(required = false)</span> String keyword,   // 可选参数</span></span><br><span class="line"><span class="params">            <span class="meta">@RequestParam(required = false)</span> String sort)</span> &#123;</span><br><span class="line">        <span class="comment">// GET /api/products/list?page=2&amp;size=20&amp;keyword=手机&amp;sort=price</span></span><br><span class="line">        <span class="keyword">return</span> productService.findByPage(page, size, keyword, sort);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 3. 请求体 @RequestBody（JSON）</span></span><br><span class="line">    <span class="comment">// 用于接收 JSON 格式的请求体</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Product&gt; <span class="title function_">create</span><span class="params">(<span class="meta">@RequestBody</span> <span class="meta">@Valid</span> Product product)</span> &#123;</span><br><span class="line">        <span class="comment">// POST /api/products  Body: &#123;&quot;name&quot;:&quot;手机&quot;,&quot;price&quot;:2999.00&#125;</span></span><br><span class="line">        <span class="keyword">return</span> Result.success(productService.save(product));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 4. 表单参数 @RequestParam（form-data）</span></span><br><span class="line">    <span class="comment">// 用于接收传统表单提交</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/form&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">submitForm</span><span class="params">(<span class="meta">@RequestParam</span> String name,</span></span><br><span class="line"><span class="params">                                 <span class="meta">@RequestParam</span> String email)</span> &#123;</span><br><span class="line">        <span class="comment">// POST /api/products/form  Body: name=手机&amp;email=xxx</span></span><br><span class="line">        <span class="keyword">return</span> Result.success();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 5. 请求头 @RequestHeader</span></span><br><span class="line">    <span class="comment">// 用于获取 HTTP 请求头中的值</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/header&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getHeader</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@RequestHeader(&quot;Authorization&quot;)</span> String auth,</span></span><br><span class="line"><span class="params">            <span class="meta">@RequestHeader(value = &quot;X-Custom-Header&quot;, required = false)</span> String custom)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Auth: &quot;</span> + auth + <span class="string">&quot;, Custom: &quot;</span> + custom;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 6. Cookie @CookieValue</span></span><br><span class="line">    <span class="comment">// 用于获取 Cookie 中的值</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/cookie&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getCookie</span><span class="params">(<span class="meta">@CookieValue(&quot;JSESSIONID&quot;)</span> String sessionId)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Session ID: &quot;</span> + sessionId;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 7. 实体类自动映射</span></span><br><span class="line">    <span class="comment">// Spring 会自动将请求参数映射到对象的同名属性</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/search&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Product&gt; <span class="title function_">search</span><span class="params">(ProductQuery query)</span> &#123;</span><br><span class="line">        <span class="comment">// GET /api/products/search?keyword=手机&amp;category=electronics&amp;minPrice=1000</span></span><br><span class="line">        <span class="keyword">return</span> productService.search(query);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 8. Servlet 原生对象</span></span><br><span class="line">    <span class="comment">// 直接注入 HttpServletRequest、HttpServletResponse 等</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/raw&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">raw</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                    HttpServletResponse response)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">method</span> <span class="operator">=</span> request.getMethod();</span><br><span class="line">        <span class="type">String</span> <span class="variable">remoteAddr</span> <span class="operator">=</span> request.getRemoteAddr();</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Method: &quot;</span> + method + <span class="string">&quot;, IP: &quot;</span> + remoteAddr;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询参数实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProductQuery</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String keyword;</span><br><span class="line">    <span class="keyword">private</span> String category;</span><br><span class="line">    <span class="keyword">private</span> BigDecimal minPrice;</span><br><span class="line">    <span class="keyword">private</span> BigDecimal maxPrice;</span><br><span class="line">    <span class="meta">@Min(1)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Integer</span> <span class="variable">page</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="meta">@Max(100)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Integer</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-5-响应处理与数据封装"><a href="#6-5-响应处理与数据封装" class="headerlink" title="6.5 响应处理与数据封装"></a>6.5 响应处理与数据封装</h3><p>为了保持 API 响应格式的一致性，我们通常会定义统一的响应结构：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 统一响应结果封装</span></span><br><span class="line"><span class="comment"> * 所有 API 响应都使用这个结构</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Result</span>&lt;T&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> code;           <span class="comment">// 状态码：200=成功，其他=失败</span></span><br><span class="line">    <span class="keyword">private</span> String message;     <span class="comment">// 消息：成功/失败的描述</span></span><br><span class="line">    <span class="keyword">private</span> T data;            <span class="comment">// 数据：响应的数据</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 成功响应（带数据）</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">success</span><span class="params">(T data)</span> &#123;</span><br><span class="line">        Result&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;();</span><br><span class="line">        result.setCode(<span class="number">200</span>);</span><br><span class="line">        result.setMessage(<span class="string">&quot;success&quot;</span>);</span><br><span class="line">        result.setData(data);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 成功响应（无数据）</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">success</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> success(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 失败响应</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">error</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> error(<span class="number">500</span>, message);  <span class="comment">// 默认 500 错误</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 带状态码的失败响应</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">error</span><span class="params">(<span class="type">int</span> code, String message)</span> &#123;</span><br><span class="line">        Result&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;();</span><br><span class="line">        result.setCode(code);</span><br><span class="line">        result.setMessage(message);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分页结果封装</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PageResult</span>&lt;T&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> total;         <span class="comment">// 总记录数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> page;          <span class="comment">// 当前页</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> size;         <span class="comment">// 每页大小</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> totalPages;   <span class="comment">// 总页数</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;T&gt; data;     <span class="comment">// 数据列表</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 从 Spring Data 的 Page 对象转换</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; PageResult&lt;T&gt; <span class="title function_">of</span><span class="params">(Page&lt;T&gt; page)</span> &#123;</span><br><span class="line">        PageResult&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">PageResult</span>&lt;&gt;();</span><br><span class="line">        result.setTotal(page.getTotalElements());</span><br><span class="line">        result.setPage(page.getNumber() + <span class="number">1</span>);  <span class="comment">// Spring Data 页码从 0 开始</span></span><br><span class="line">        result.setSize(page.getSize());</span><br><span class="line">        result.setTotalPages(page.getTotalPages());</span><br><span class="line">        result.setData(page.getContent());</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-6-统一异常处理"><a href="#6-6-统一异常处理" class="headerlink" title="6.6 统一异常处理"></a>6.6 统一异常处理</h3><p>为了给用户友好的错误提示，我们需要统一处理各种异常：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 全局异常处理器</span></span><br><span class="line"><span class="comment"> * 统一处理所有 Controller 抛出的异常</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestControllerAdvice</span>  <span class="comment">// = @ControllerAdvice + @ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GlobalExceptionHandler</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 处理业务异常</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(BusinessException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleBusinessException</span><span class="params">(BusinessException e)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;业务异常：&quot;</span> + e.getMessage());</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">400</span>, e.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 处理参数校验异常</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(MethodArgumentNotValidException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleValidationException</span><span class="params">(</span></span><br><span class="line"><span class="params">            MethodArgumentNotValidException e)</span> &#123;</span><br><span class="line">        <span class="comment">// 获取第一个校验错误的信息</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> e.getBindingResult().getFieldError()</span><br><span class="line">                .getDefaultMessage();</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">400</span>, <span class="string">&quot;参数校验失败：&quot;</span> + message);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 处理资源不存在异常</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(EntityNotFoundException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleNotFound</span><span class="params">(EntityNotFoundException e)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">404</span>, e.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 处理数据库唯一约束异常</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(DataIntegrityViolationException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleDataIntegrityViolation</span><span class="params">(</span></span><br><span class="line"><span class="params">            DataIntegrityViolationException e)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">400</span>, <span class="string">&quot;数据重复，请检查后重试&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 处理其他所有异常</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">handleException</span><span class="params">(Exception e)</span> &#123;</span><br><span class="line">        e.printStackTrace();  <span class="comment">// 打印堆栈，方便排查</span></span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="number">500</span>, <span class="string">&quot;系统异常，请稍后重试&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义业务异常</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BusinessException</span> <span class="keyword">extends</span> <span class="title class_">RuntimeException</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BusinessException</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 实体未找到异常</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EntityNotFoundException</span> <span class="keyword">extends</span> <span class="title class_">RuntimeException</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">EntityNotFoundException</span><span class="params">(String entity, Long id)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(entity + <span class="string">&quot; 不存在，ID：&quot;</span> + id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、Spring-Boot-快速开发"><a href="#七、Spring-Boot-快速开发" class="headerlink" title="七、Spring Boot 快速开发"></a>七、Spring Boot 快速开发</h2><h3 id="7-1-什么是-Spring-Boot？"><a href="#7-1-什么是-Spring-Boot？" class="headerlink" title="7.1 什么是 Spring Boot？"></a>7.1 什么是 Spring Boot？</h3><p>Spring Boot 是 Spring 的<strong>子项目</strong>，它的目标是<strong>简化 Spring 应用的创建和开发过程</strong>。你可以把 Spring Boot 理解为”Spring 的一键启动器”。</p><p><strong>Spring Boot 的核心特性：</strong></p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🚀 Spring Boot 特性"] --> B["☕️ 开箱即用"]    A --> C["🎯 习惯优于配置"]    A --> D["🛠️ 内嵌服务器"]    A --> E["📊 自动配置"]    A --> F["🧪 内置测试"]        B --> B1["引入依赖\n直接运行"]        C --> C1["无需 XML 配置\nJava Config 优先"]        D --> D1["Tomcat/Jetty\n无需部署 WAR"]        E --> E1["自动配置\nSpring/Spring MVC"]        F --> F1["JUnit/MockMvc\n自动化测试"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><p><strong>1️⃣ 开箱即用：</strong><br>Spring Boot 提供了大量的”起步依赖”（Starter Dependencies），你只需要引入一个依赖，就能获得完整的模块功能。</p><p><strong>2️⃣ 习惯优于配置：</strong><br>Spring Boot 会根据你引入的依赖和类路径，自动配置应用程序。你不需要手动配置每一个 Bean。</p><p><strong>3️⃣ 内嵌服务器：</strong><br>Spring Boot 内嵌了 Tomcat、Jetty 等 Web 服务器，可以直接打包成可执行的 JAR 文件运行，无需部署到外部服务器。</p><p><strong>4️⃣ 自动配置：</strong><br>Spring Boot 会根据你的环境自动配置 Bean，例如：</p><ul><li>如果类路径中有 MySQL 驱动，自动配置数据源</li><li>如果类路径中有 Spring MVC，自动配置 DispatcherServlet</li></ul><p><strong>5️⃣ 内置测试：</strong><br>Spring Boot 提供了完整的测试支持，可以方便地编写集成测试和单元测试。</p><h3 id="7-2-Spring-Boot-项目结构"><a href="#7-2-Spring-Boot-项目结构" class="headerlink" title="7.2 Spring Boot 项目结构"></a>7.2 Spring Boot 项目结构</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">my-spring-boot-project/</span><br><span class="line">├── src/</span><br><span class="line">│   ├── main/</span><br><span class="line">│   │   ├── java/com/example/</span><br><span class="line">│   │   │   ├── Application.java              # 启动类</span><br><span class="line">│   │   │   ├── controller/                  # 控制器层（接收请求）</span><br><span class="line">│   │   │   ├── service/                     # 服务层（业务逻辑）</span><br><span class="line">│   │   │   ├── repository/                  # 数据访问层（操作数据库）</span><br><span class="line">│   │   │   ├── entity/                      # 实体类（数据库表映射）</span><br><span class="line">│   │   │   ├── dto/                         # 数据传输对象</span><br><span class="line">│   │   │   ├── config/                      # 配置类</span><br><span class="line">│   │   │   └── exception/                   # 异常处理</span><br><span class="line">│   │   └── resources/</span><br><span class="line">│   │       ├── application.yml             # 配置文件</span><br><span class="line">│   │       ├── static/                      # 静态资源（CSS/JS/图片）</span><br><span class="line">│   │       └── templates/                    # 模板文件</span><br><span class="line">│   └── test/                                # 测试代码</span><br><span class="line">├── pom.xml                                   # Maven 配置</span><br><span class="line">└── README.md                                 # 项目说明</span><br></pre></td></tr></table></figure><p><strong>分层职责说明：</strong></p><table><thead><tr><th>层级</th><th>职责</th><th>包含内容</th></tr></thead><tbody><tr><td><strong>Controller</strong></td><td>接收请求、参数校验、调用服务</td><td>@RestController、@GetMapping 等</td></tr><tr><td><strong>Service</strong></td><td>业务逻辑处理、事务管理</td><td>@Service、@Transactional</td></tr><tr><td><strong>Repository</strong></td><td>数据访问、操作数据库</td><td>JpaRepository、MyBatis Mapper</td></tr><tr><td><strong>Entity</strong></td><td>数据模型、ORM 映射</td><td>@Entity、@Table、@Column</td></tr></tbody></table><h3 id="7-3-Spring-Boot-启动类"><a href="#7-3-Spring-Boot-启动类" class="headerlink" title="7.3 Spring Boot 启动类"></a>7.3 Spring Boot 启动类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Boot 启动类</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@SpringBootApplication</span> 是一个组合注解，包含：</span></span><br><span class="line"><span class="comment"> * - <span class="doctag">@Configuration</span>：标记为配置类</span></span><br><span class="line"><span class="comment"> * - <span class="doctag">@EnableAutoConfiguration</span>：启用自动配置</span></span><br><span class="line"><span class="comment"> * - <span class="doctag">@ComponentScan</span>：扫描当前包及子包的组件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Application</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 启动 Spring Boot 应用</span></span><br><span class="line">        SpringApplication.run(Application.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义 SpringApplication 配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomApplication</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建 SpringApplication 实例</span></span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(Application.class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 关闭 Banner（启动时的 ASCII 艺术字）</span></span><br><span class="line">        app.setBannerMode(Banner.Mode.OFF);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置自定义 Banner</span></span><br><span class="line">        app.setBanner(<span class="keyword">new</span> <span class="title class_">Banner</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">printBanner</span><span class="params">(Environment environment, </span></span><br><span class="line"><span class="params">                                  Class&lt;?&gt; sourceClass, </span></span><br><span class="line"><span class="params">                                  PrintStream out)</span> &#123;</span><br><span class="line">                out.println(<span class="string">&quot;========================================&quot;</span>);</span><br><span class="line">                out.println(<span class="string">&quot;  🚀 我的 Spring Boot 应用 🚀&quot;</span>);</span><br><span class="line">                out.println(<span class="string">&quot;========================================&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 运行应用</span></span><br><span class="line">        app.run(args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-4-Spring-Boot-Maven-配置"><a href="#7-4-Spring-Boot-Maven-配置" class="headerlink" title="7.4 Spring Boot Maven 配置"></a>7.4 Spring Boot Maven 配置</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">        parent 元素指定父 POM</span></span><br><span class="line"><span class="comment">        Spring Boot 会统一管理所有依赖的版本</span></span><br><span class="line"><span class="comment">        我们只需要指定版本号，不需要写版本号（除非跟父 POM 不一致）</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.example<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>my-spring-boot-demo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>17<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">            1. Web 开发起步依赖</span></span><br><span class="line"><span class="comment">            包含：Spring MVC、Tomcat、Jackson JSON 等</span></span><br><span class="line"><span class="comment">            一行依赖搞定所有 Web 开发需要的东西</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">            2. Spring Data JPA 起步依赖</span></span><br><span class="line"><span class="comment">            包含：Hibernate、Spring ORM、Spring Data JPA</span></span><br><span class="line"><span class="comment">            简化数据访问层开发</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">            3. MySQL 驱动</span></span><br><span class="line"><span class="comment">            scope=runtime：运行时需要，编译时不需要</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-j<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>runtime<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 4. Redis 缓存 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 5. 模板引擎 Thymeleaf --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-thymeleaf<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">            6. 参数校验起步依赖</span></span><br><span class="line"><span class="comment">            包含：Hibernate Validator</span></span><br><span class="line"><span class="comment">            支持 @Valid、@NotNull 等校验注解</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-validation<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">            7. Lombok</span></span><br><span class="line"><span class="comment">            自动生成 getter/setter/toString 等方法</span></span><br><span class="line"><span class="comment">            大幅减少样板代码</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 8. 测试起步依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- </span></span><br><span class="line"><span class="comment">            9. 开发工具</span></span><br><span class="line"><span class="comment">            支持热部署（修改代码后自动重启）</span></span><br><span class="line"><span class="comment">            显著提升开发效率</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-devtools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>runtime<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- Spring Boot Maven 插件：打包成可执行 JAR --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                    <span class="comment">&lt;!-- 打包时排除 Lombok，因为运行时不需要 --&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">exclude</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="八、Spring-Boot-核心配置"><a href="#八、Spring-Boot-核心配置" class="headerlink" title="八、Spring Boot 核心配置"></a>八、Spring Boot 核心配置</h2><h3 id="8-1-application-yml-配置详解"><a href="#8-1-application-yml-配置详解" class="headerlink" title="8.1 application.yml 配置详解"></a>8.1 application.yml 配置详解</h3><p>Spring Boot 使用 <code>application.yml</code>（或 <code>application.properties</code>）作为配置文件：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># 服务器配置</span></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8080</span>                    <span class="comment"># 服务端口，默认 8080</span></span><br><span class="line">  <span class="attr">servlet:</span></span><br><span class="line">    <span class="attr">context-path:</span> <span class="string">/api</span>          <span class="comment"># 上下文路径，即 URL 前缀</span></span><br><span class="line">  <span class="attr">tomcat:</span></span><br><span class="line">    <span class="attr">threads:</span></span><br><span class="line">      <span class="attr">max:</span> <span class="number">200</span>                  <span class="comment"># 最大线程数</span></span><br><span class="line">      <span class="attr">min-spare:</span> <span class="number">10</span>             <span class="comment"># 最小空闲线程</span></span><br><span class="line">    <span class="attr">connection-timeout:</span> <span class="number">20000</span>   <span class="comment"># 连接超时时间（毫秒）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># Spring 配置</span></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">my-spring-boot-demo</span>   <span class="comment"># 应用名称，用于服务注册等</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment"># 数据源配置</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/demo?useSSL=false&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">your_password</span></span><br><span class="line">    <span class="attr">hikari:</span></span><br><span class="line">      <span class="attr">maximum-pool-size:</span> <span class="number">20</span>     <span class="comment"># 连接池最大连接数</span></span><br><span class="line">      <span class="attr">minimum-idle:</span> <span class="number">5</span>            <span class="comment"># 连接池最小空闲连接</span></span><br><span class="line">      <span class="attr">connection-timeout:</span> <span class="number">30000</span> <span class="comment"># 获取连接超时（毫秒）</span></span><br><span class="line">      <span class="attr">idle-timeout:</span> <span class="number">600000</span>      <span class="comment"># 空闲连接超时</span></span><br><span class="line">      <span class="attr">max-lifetime:</span> <span class="number">1800000</span>     <span class="comment"># 连接最大生命周期</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment"># JPA 配置（Java Persistence API）</span></span><br><span class="line">  <span class="attr">jpa:</span></span><br><span class="line">    <span class="attr">hibernate:</span></span><br><span class="line">      <span class="attr">ddl-auto:</span> <span class="string">update</span>          <span class="comment"># 表结构自动更新：</span></span><br><span class="line">                               <span class="comment"># update：自动更新（推荐开发）</span></span><br><span class="line">                               <span class="comment"># create：每次删除并重新创建</span></span><br><span class="line">                               <span class="comment"># none：不自动更新</span></span><br><span class="line">                               <span class="comment"># validate：校验表结构</span></span><br><span class="line">    <span class="attr">show-sql:</span> <span class="literal">true</span>              <span class="comment"># 显示 SQL 语句（方便调试）</span></span><br><span class="line">    <span class="attr">properties:</span></span><br><span class="line">      <span class="attr">hibernate:</span></span><br><span class="line">        <span class="attr">format_sql:</span> <span class="literal">true</span>        <span class="comment"># 格式化 SQL 输出</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment"># Redis 配置</span></span><br><span class="line">  <span class="attr">data:</span></span><br><span class="line">    <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">host:</span> <span class="string">localhost</span>          <span class="comment"># Redis 主机地址</span></span><br><span class="line">      <span class="attr">port:</span> <span class="number">6379</span>              <span class="comment"># Redis 端口</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">your_password</span> <span class="comment"># Redis 密码（如果没有可省略）</span></span><br><span class="line">      <span class="attr">lettuce:</span></span><br><span class="line">        <span class="attr">pool:</span></span><br><span class="line">          <span class="attr">max-active:</span> <span class="number">8</span>       <span class="comment"># 最大连接数</span></span><br><span class="line">          <span class="attr">max-idle:</span> <span class="number">8</span>          <span class="comment"># 最大空闲连接</span></span><br><span class="line">          <span class="attr">min-idle:</span> <span class="number">0</span>          <span class="comment"># 最小空闲连接</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment"># 日志配置</span></span><br><span class="line">  <span class="attr">logging:</span></span><br><span class="line">    <span class="attr">level:</span></span><br><span class="line">      <span class="attr">root:</span> <span class="string">INFO</span>              <span class="comment"># 根日志级别</span></span><br><span class="line">      <span class="attr">com.example:</span> <span class="string">DEBUG</span>     <span class="comment"># 指定包的日志级别</span></span><br><span class="line">      <span class="attr">org.hibernate.SQL:</span> <span class="string">DEBUG</span>  <span class="comment"># Hibernate SQL 日志</span></span><br><span class="line">    <span class="attr">pattern:</span></span><br><span class="line">      <span class="attr">console:</span> <span class="string">&quot;%d&#123;yyyy-MM-dd HH:mm:ss&#125; [%thread] %-5level %logger&#123;36&#125; - %msg%n&quot;</span></span><br><span class="line">    <span class="attr">file:</span></span><br><span class="line">      <span class="attr">name:</span> <span class="string">logs/app.log</span>     <span class="comment"># 日志文件路径</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># 自定义配置</span></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="attr">app:</span></span><br><span class="line">  <span class="attr">version:</span> <span class="number">1.0</span><span class="number">.0</span></span><br><span class="line">  <span class="attr">author:</span> <span class="string">旅人</span></span><br><span class="line">  <span class="attr">features:</span></span><br><span class="line">    <span class="attr">email-enabled:</span> <span class="literal">true</span>      <span class="comment"># 邮件功能开关</span></span><br><span class="line">    <span class="attr">sms-enabled:</span> <span class="literal">false</span>        <span class="comment"># 短信功能开关</span></span><br></pre></td></tr></table></figure><h3 id="8-2-多环境配置"><a href="#8-2-多环境配置" class="headerlink" title="8.2 多环境配置"></a>8.2 多环境配置</h3><p>在真实开发中，我们通常有多个环境（开发、测试、生产），每个环境的配置可能不同：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml - 主配置（公共配置）</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">my-spring-boot-demo</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># 开发环境配置（application-dev.yml）</span></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># spring:</span></span><br><span class="line"><span class="comment">#   datasource:</span></span><br><span class="line"><span class="comment">#     url: jdbc:mysql://localhost:3306/dev_db</span></span><br><span class="line"><span class="comment">#     username: dev_user</span></span><br><span class="line"><span class="comment">#     password: dev_password</span></span><br><span class="line"><span class="comment">#   jpa:</span></span><br><span class="line"><span class="comment">#     show-sql: true</span></span><br><span class="line"><span class="comment"># logging:</span></span><br><span class="line"><span class="comment">#   level:</span></span><br><span class="line"><span class="comment">#     com.example: DEBUG</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># 生产环境配置（application-prod.yml）</span></span><br><span class="line"><span class="comment"># ==========================================</span></span><br><span class="line"><span class="comment"># spring:</span></span><br><span class="line"><span class="comment">#   datasource:</span></span><br><span class="line"><span class="comment">#     url: jdbc:mysql://prod-db-server:3306/prod_db</span></span><br><span class="line"><span class="comment">#     username: prod_user</span></span><br><span class="line"><span class="comment">#     password: $&#123;DB_PASSWORD&#125;  # 从环境变量读取</span></span><br><span class="line"><span class="comment">#   jpa:</span></span><br><span class="line"><span class="comment">#     show-sql: false</span></span><br><span class="line"><span class="comment"># logging:</span></span><br><span class="line"><span class="comment">#   level:</span></span><br><span class="line"><span class="comment">#     com.example: INFO</span></span><br></pre></td></tr></table></figure><p><strong>激活特定环境的方式：</strong></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方式一：在 application.yml 中指定</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">profiles:</span></span><br><span class="line">    <span class="attr">active:</span> <span class="string">dev</span>  <span class="comment"># 激活开发环境</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方式二：通过命令行参数指定</span></span><br><span class="line">java -jar myapp.jar --spring.profiles.active=prod</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式三：在启动类中指定</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ActiveProfiles(&quot;dev&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Application</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(Application.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-ConfigurationProperties-配置绑定"><a href="#8-3-ConfigurationProperties-配置绑定" class="headerlink" title="8.3 @ConfigurationProperties 配置绑定"></a>8.3 @ConfigurationProperties 配置绑定</h3><p>将配置文件中的属性绑定到 Java 对象，便于管理和使用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 配置属性类</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ConfigurationProperties</span> 会自动将 app.* 前缀的属性绑定到这个类</span></span><br><span class="line"><span class="comment"> * 例如：app.version -&gt; version, app.author -&gt; author</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;app&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppProperties</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String version;           <span class="comment">// 对应 app.version</span></span><br><span class="line">    <span class="keyword">private</span> String author;          <span class="comment">// 对应 app.author</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Features</span> <span class="variable">features</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Features</span>();  <span class="comment">// 嵌套对象</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Data</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Features</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">boolean</span> emailEnabled;   <span class="comment">// 对应 app.features.email-enabled</span></span><br><span class="line">        <span class="keyword">private</span> <span class="type">boolean</span> smsEnabled;     <span class="comment">// 对应 app.features.sms-enabled</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用配置类（推荐方式）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(AppProperties.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> &#123;</span><br><span class="line">    <span class="comment">// AppProperties 已自动注册为 Bean，可以直接注入使用</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AppProperties appProperties;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendNotification</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (appProperties.getFeatures().isEmailEnabled()) &#123;</span><br><span class="line">            <span class="comment">// 发送邮件通知</span></span><br><span class="line">            emailService.send();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (appProperties.getFeatures().isSmsEnabled()) &#123;</span><br><span class="line">            <span class="comment">// 发送短信通知</span></span><br><span class="line">            smsService.send();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、Spring-Data-简化数据访问"><a href="#九、Spring-Data-简化数据访问" class="headerlink" title="九、Spring Data 简化数据访问"></a>九、Spring Data 简化数据访问</h2><h3 id="9-1-Spring-Data-JPA-详解"><a href="#9-1-Spring-Data-JPA-详解" class="headerlink" title="9.1 Spring Data JPA 详解"></a>9.1 Spring Data JPA 详解</h3><p>Spring Data JPA 是 Spring Data 项目的一部分，它<strong>极大简化了数据访问层的开发</strong>。通过继承 <code>JpaRepository</code>，你无需编写实现类，就能获得完整的 CRUD 功能。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Data JPA Repository</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * JpaRepository&lt;EntityType, PrimaryKeyType&gt;</span></span><br><span class="line"><span class="comment"> * - EntityType：实体类类型</span></span><br><span class="line"><span class="comment"> * - PrimaryKeyType：主键类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 基本 CRUD（ JpaRepository 已提供，无需编写）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserRepository</span> <span class="keyword">extends</span> <span class="title class_">JpaRepository</span>&lt;User, Long&gt; &#123;</span><br><span class="line">    <span class="comment">// findById, save, deleteById, findAll 等方法已自动提供</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 自动方法名解析查询</span></span><br><span class="line"><span class="comment">// Spring Data 会根据方法名自动生成 SQL</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserRepository</span> <span class="keyword">extends</span> <span class="title class_">JpaRepository</span>&lt;User, Long&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE username = ?</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByUsername</span><span class="params">(String username)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE status = ?</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByStatus</span><span class="params">(Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE email = ?</span></span><br><span class="line">    User <span class="title function_">findByEmail</span><span class="params">(String email)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE username LIKE &#x27;%keyword%&#x27;</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByUsernameContaining</span><span class="params">(String keyword)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE status = ? AND username LIKE &#x27;%keyword%&#x27;</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByStatusAndUsernameContaining</span><span class="params">(Integer status, String keyword)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 分页查询</span></span><br><span class="line">    Page&lt;User&gt; <span class="title function_">findByStatus</span><span class="params">(Integer status, Pageable pageable)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 排序查询</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByStatusOrderByCreatedAtDesc</span><span class="params">(Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 统计查询</span></span><br><span class="line">    <span class="type">long</span> <span class="title function_">countByStatus</span><span class="params">(Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 存在性查询</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">existsByEmail</span><span class="params">(String email)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 自定义 HQL/JPQL 查询</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserRepository</span> <span class="keyword">extends</span> <span class="title class_">JpaRepository</span>&lt;User, Long&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 使用 @Query 注解自定义查询</span></span><br><span class="line">    <span class="comment">// :status 是命名参数，等同于 ?1</span></span><br><span class="line">    <span class="meta">@Query(&quot;SELECT u FROM User u WHERE u.status = :status ORDER BY u.createdAt DESC&quot;)</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findUsersByStatus</span><span class="params">(<span class="meta">@Param(&quot;status&quot;)</span> Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 聚合查询</span></span><br><span class="line">    <span class="meta">@Query(&quot;SELECT COUNT(u) FROM User u WHERE u.status = :status&quot;)</span></span><br><span class="line">    <span class="type">long</span> <span class="title function_">countByStatusQuery</span><span class="params">(<span class="meta">@Param(&quot;status&quot;)</span> Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 原生 SQL 查询（nativeQuery = true）</span></span><br><span class="line">    <span class="meta">@Query(value = &quot;SELECT * FROM users WHERE status = :status&quot;, nativeQuery = true)</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findUsersByStatusNative</span><span class="params">(<span class="meta">@Param(&quot;status&quot;)</span> Integer status)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 使用 Specification 进行复杂查询</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserRepository</span> <span class="keyword">extends</span> <span class="title class_">JpaRepository</span>&lt;User, Long&gt;,</span><br><span class="line">        JpaSpecificationExecutor&lt;User&gt; &#123;</span><br><span class="line">    <span class="comment">// JpaSpecificationExecutor 提供了 findAll(Specification) 方法</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 Specification 进行动态查询</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 动态查询：根据条件组合构建查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">search</span><span class="params">(UserSearchCriteria criteria)</span> &#123;</span><br><span class="line">        <span class="comment">// 构建 Specification</span></span><br><span class="line">        Specification&lt;User&gt; spec = (root, query, cb) -&gt; &#123;</span><br><span class="line">            List&lt;Predicate&gt; predicates = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 关键字模糊查询</span></span><br><span class="line">            <span class="keyword">if</span> (criteria.getKeyword() != <span class="literal">null</span>) &#123;</span><br><span class="line">                predicates.add(cb.like(root.get(<span class="string">&quot;username&quot;</span>), </span><br><span class="line">                    <span class="string">&quot;%&quot;</span> + criteria.getKeyword() + <span class="string">&quot;%&quot;</span>));</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 状态精确查询</span></span><br><span class="line">            <span class="keyword">if</span> (criteria.getStatus() != <span class="literal">null</span>) &#123;</span><br><span class="line">                predicates.add(cb.equal(root.get(<span class="string">&quot;status&quot;</span>), </span><br><span class="line">                    criteria.getStatus()));</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 日期范围查询</span></span><br><span class="line">            <span class="keyword">if</span> (criteria.getStartDate() != <span class="literal">null</span>) &#123;</span><br><span class="line">                predicates.add(cb.greaterThanOrEqualTo(root.get(<span class="string">&quot;createdAt&quot;</span>), </span><br><span class="line">                    criteria.getStartDate()));</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 使用 AND 组合所有条件</span></span><br><span class="line">            <span class="keyword">return</span> cb.and(predicates.toArray(<span class="keyword">new</span> <span class="title class_">Predicate</span>[<span class="number">0</span>]));</span><br><span class="line">        &#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 执行查询</span></span><br><span class="line">        <span class="keyword">return</span> userRepository.findAll(spec);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-2-Spring-Data-Redis-缓存操作"><a href="#9-2-Spring-Data-Redis-缓存操作" class="headerlink" title="9.2 Spring Data Redis 缓存操作"></a>9.2 Spring Data Redis 缓存操作</h3><p>Spring Data Redis 提供了简洁的 API 来操作 Redis：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Data Redis 操作示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate redisTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// String（字符串）操作</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setString</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        <span class="comment">// 设置字符串</span></span><br><span class="line">        redisTemplate.opsForValue().set(key, value);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置字符串并指定过期时间</span></span><br><span class="line">        redisTemplate.opsForValue().set(key, value, Duration.ofHours(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getString</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForValue().get(key);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// Hash（哈希表）操作</span></span><br><span class="line">    <span class="comment">// 适用于存储对象</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setHash</span><span class="params">(String key, String field, String value)</span> &#123;</span><br><span class="line">        redisTemplate.opsForHash().put(key, field, value);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getHash</span><span class="params">(String key, String field)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForHash().get(key, field);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// List（列表）操作</span></span><br><span class="line">    <span class="comment">// 适用于队列、栈等场景</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">leftPush</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        <span class="comment">// 左侧插入</span></span><br><span class="line">        redisTemplate.opsForList().leftPush(key, value);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">rightPop</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="comment">// 右侧弹出</span></span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForList().rightPop(key);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// Set（集合）操作</span></span><br><span class="line">    <span class="comment">// 适用于去重、标签等场景</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addToSet</span><span class="params">(String key, String... values)</span> &#123;</span><br><span class="line">        redisTemplate.opsForSet().add(key, values);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Set&lt;String&gt; <span class="title function_">getSetMembers</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForSet().members(key);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    <span class="comment">// 缓存注解示例</span></span><br><span class="line">    <span class="comment">// ==========================================</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 查询缓存：先查缓存，缓存不存在则查数据库并写入缓存</span></span><br><span class="line">    <span class="meta">@Cacheable(value = &quot;users&quot;, key = &quot;#id&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUserById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.findById(id).orElse(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 更新缓存：执行方法后更新缓存</span></span><br><span class="line">    <span class="meta">@CachePut(value = &quot;users&quot;, key = &quot;#result.id&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">saveUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.save(user);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 清除缓存：执行方法后清除缓存</span></span><br><span class="line">    <span class="meta">@CacheEvict(value = &quot;users&quot;, key = &quot;#id&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUser</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        userRepository.deleteById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 清除所有缓存</span></span><br><span class="line">    <span class="meta">@CacheEvict(value = &quot;users&quot;, allEntries = true)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clearUserCache</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 清空所有 &quot;users&quot; 缓存</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十、Spring-Boot-日志与监控"><a href="#十、Spring-Boot-日志与监控" class="headerlink" title="十、Spring Boot 日志与监控"></a>十、Spring Boot 日志与监控</h2><h3 id="10-1-日志配置"><a href="#10-1-日志配置" class="headerlink" title="10.1 日志配置"></a>10.1 日志配置</h3><p>Spring Boot 默认使用 Logback 作为日志框架，我们可以通过 <code>application.yml</code> 进行配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">logging:</span></span><br><span class="line">  <span class="attr">level:</span></span><br><span class="line">    <span class="attr">root:</span> <span class="string">INFO</span>                  <span class="comment"># 根日志级别：INFO 及以上</span></span><br><span class="line">    <span class="attr">com.example:</span> <span class="string">DEBUG</span>          <span class="comment"># 指定包：DEBUG 及以上</span></span><br><span class="line">    <span class="attr">org.springframework.web:</span> <span class="string">DEBUG</span>  <span class="comment"># Spring Web 详细日志</span></span><br><span class="line">    <span class="attr">org.hibernate.SQL:</span> <span class="string">DEBUG</span>     <span class="comment"># Hibernate SQL 日志</span></span><br><span class="line">    <span class="attr">org.hibernate.type.descriptor.sql.BasicBinder:</span> <span class="string">TRACE</span>  <span class="comment"># SQL 参数绑定详情</span></span><br><span class="line">  <span class="attr">pattern:</span></span><br><span class="line">    <span class="comment"># 控制台输出格式</span></span><br><span class="line">    <span class="attr">console:</span> <span class="string">&quot;%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;36&#125; - %msg%n&quot;</span></span><br><span class="line">    <span class="comment"># 文件输出格式</span></span><br><span class="line">    <span class="attr">file:</span> <span class="string">&quot;%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;36&#125; - %msg%n&quot;</span></span><br><span class="line">  <span class="attr">file:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">logs/application.log</span>  <span class="comment"># 日志文件路径</span></span><br><span class="line">    <span class="attr">max-size:</span> <span class="string">10MB</span>              <span class="comment"># 单个日志文件最大 10MB</span></span><br><span class="line">    <span class="attr">max-history:</span> <span class="number">30</span>             <span class="comment"># 保留最近 30 天的日志</span></span><br></pre></td></tr></table></figure><h3 id="10-2-Spring-Boot-Actuator-监控端点"><a href="#10-2-Spring-Boot-Actuator-监控端点" class="headerlink" title="10.2 Spring Boot Actuator 监控端点"></a>10.2 Spring Boot Actuator 监控端点</h3><p>Spring Boot Actuator 提供了生产级别的监控功能：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 添加 Actuator 依赖 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-actuator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开启 Actuator 端点</span></span><br><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoints:</span></span><br><span class="line">    <span class="attr">web:</span></span><br><span class="line">      <span class="attr">exposure:</span></span><br><span class="line">        <span class="attr">include:</span> <span class="string">health,info,metrics,env,beans,caches,conditions</span></span><br><span class="line">  <span class="attr">endpoint:</span></span><br><span class="line">    <span class="attr">health:</span></span><br><span class="line">      <span class="attr">show-details:</span> <span class="string">when_authorized</span>  <span class="comment"># 健康检查详情（需要授权才能看）</span></span><br><span class="line">  <span class="attr">health:</span></span><br><span class="line">    <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span>   <span class="comment"># 包含 Redis 健康检查</span></span><br><span class="line">    <span class="attr">db:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span>   <span class="comment"># 包含数据库健康检查</span></span><br></pre></td></tr></table></figure><p><strong>常用 Actuator 端点：</strong></p><table><thead><tr><th>端点</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>/actuator/health</code></td><td>应用健康状态</td><td>查看应用是否正常运行</td></tr><tr><td><code>/actuator/info</code></td><td>应用信息</td><td>查看应用基本信息</td></tr><tr><td><code>/actuator/metrics</code></td><td>性能指标</td><td>查看内存、CPU、请求数等</td></tr><tr><td><code>/actuator/env</code></td><td>环境变量</td><td>查看所有配置的环境变量</td></tr><tr><td><code>/actuator/beans</code></td><td>所有 Bean</td><td>查看容器中所有 Bean</td></tr><tr><td><code>/actuator/mappings</code></td><td>所有 URL 映射</td><td>查看所有接口路径</td></tr><tr><td><code>/actuator/conditions</code></td><td>配置条件</td><td>查看自动配置结果</td></tr></tbody></table><hr><h2 id="十一、常见问题与最佳实践"><a href="#十一、常见问题与最佳实践" class="headerlink" title="十一、常见问题与最佳实践"></a>十一、常见问题与最佳实践</h2><h3 id="11-1-常见问题与解决方案"><a href="#11-1-常见问题与解决方案" class="headerlink" title="11.1 常见问题与解决方案"></a>11.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["⚠️ 循环依赖"]    A --> C["⚠️ 事务不生效"]    A --> D["⚠️ 启动失败"]    A --> E["⚠️ 中文乱码"]        B --> B1["A 注入 B\nB 注入 A"]    B1 --> B2["解决：\n@Lazy 延迟加载\n或重构设计"]        C --> C1["内部方法调用\n使用代理对象"]    C1 --> C2["解决方案：\n注入自身代理对象\n或使用 AopContext"]        D --> D1["Bean 注入失败\n配置错误"]    D1 --> D2["解决方案：\n查看启动日志\n使用 --debug 启动"]        E --> E1["编码不一致"]    E1 --> E2["解决方案：\n配置 UTF-8\napplication.yml"]        style A fill:#fff3e0</pre></div><p><strong>问题一：循环依赖（Circular Dependency）</strong></p><p>当两个 Bean 互相依赖时会形成循环：</p><ul><li>A Bean 构造器需要注入 B</li><li>B Bean 构造器需要注入 A</li></ul><p><strong>解决方案：</strong></p><ol><li>使用 <code>@Lazy</code> 延迟加载其中一个依赖</li><li>重构代码，消除循环依赖</li><li>使用 Setter 注入代替构造器注入</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 解决方案：@Lazy 延迟加载</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> B b;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">A</span><span class="params">(<span class="meta">@Lazy</span> B b)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.b = b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>问题二：事务不生效</strong></p><p>常见原因是内部方法调用（<code>this.method()</code>）不经过代理对象。</p><p><strong>解决方案：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService self;  <span class="comment">// 注入自身代理对象</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">outerMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        self.innerMethod();  <span class="comment">// 通过代理对象调用</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">innerMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 现在有事务了</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="11-2-Spring-Boot-开发建议"><a href="#11-2-Spring-Boot-开发建议" class="headerlink" title="11.2 Spring Boot 开发建议"></a>11.2 Spring Boot 开发建议</h3><table><thead><tr><th>实践</th><th>说明</th><th>推荐程度</th></tr></thead><tbody><tr><td><strong>分层清晰</strong></td><td>Controller → Service → Repository</td><td>✅✅✅</td></tr><tr><td><strong>统一响应格式</strong></td><td>所有接口返回 Result<T></td><td>✅✅✅</td></tr><tr><td><strong>全局异常处理</strong></td><td>@ControllerAdvice 统一处理异常</td><td>✅✅✅</td></tr><tr><td><strong>参数校验</strong></td><td>@Valid + BindingResult</td><td>✅✅✅</td></tr><tr><td><strong>事务合理使用</strong></td><td>不要在事务中做耗时操作</td><td>✅✅✅</td></tr><tr><td><strong>日志规范</strong></td><td>使用 SLF4J，合理配置日志级别</td><td>✅✅</td></tr><tr><td><strong>配置文件分离</strong></td><td>多环境配置，敏感信息加密</td><td>✅✅</td></tr></tbody></table><hr><h2 id="十二、总结"><a href="#十二、总结" class="headerlink" title="十二、总结"></a>十二、总结</h2><h3 id="12-1-核心知识点回顾"><a href="#12-1-核心知识点回顾" class="headerlink" title="12.1 核心知识点回顾"></a>12.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Spring Framework))    Spring Core      IoC 容器      DI 依赖注入      Bean 生命周期      作用域    Spring AOP      切面 Aspect      切入点 Pointcut      通知 Advice      织入 Weaving    Spring MVC      DispatchServlet      HandlerMapping      Controller      ViewResolver    Spring Boot      自动配置      起步依赖      内嵌服务器    Spring Data      JPA Repository      Redis 操作      查询方法    事务管理      @Transactional      传播行为      隔离级别</pre></div><h3 id="12-2-Spring-学习路线"><a href="#12-2-Spring-学习路线" class="headerlink" title="12.2 Spring 学习路线"></a>12.2 Spring 学习路线</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["Spring 学习路线"] --> B["第一阶段\nSpring Core"]    B --> C["第二阶段\nSpring MVC"]    C --> D["第三阶段\nSpring Boot"]    D --> E["第四阶段\nSpring Cloud"]        B --> B1["IoC/DI"]    B --> B2["Bean 管理"]    B --> B3["AOP"]        C --> C1["请求映射"]    C --> C2["参数绑定"]    C --> C3["视图解析"]        D --> D1["自动配置"]    D --> D2["数据访问"]    D --> D3["Web 开发"]        E --> E1["微服务架构"]    E --> E2["服务治理"]    E --> E3["分布式事务"]        style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><h3 id="12-3-下一步推荐学习"><a href="#12-3-下一步推荐学习" class="headerlink" title="12.3 下一步推荐学习"></a>12.3 下一步推荐学习</h3><ul><li>📖 <strong>Spring Cloud 微服务</strong>：服务注册、配置中心、熔断器、网关</li><li>📖 <strong>Spring Security 安全</strong>：认证授权、OAuth2、JWT</li><li>📖 <strong>Spring Batch 大数据</strong>：批处理框架</li><li>📖 <strong>Spring Integration</strong>：消息集成</li><li>📖 <strong>Spring 源码解读</strong>：深入理解框架设计思想</li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Spring 是 Java 开发者的必备技能，它不仅简化了企业级开发，更提供了一种优秀的软件设计思想。从 Spring Framework 到 Spring Boot，再到 Spring Cloud，Spring 生态已经成为了 Java 后端开发的事实标准。希望本文能帮助你建立完整的 Spring 技术体系，为今后的深入学习打下坚实基础！💪</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/spring-framework-guide/</id>
    <link href="https://blog.codenav.top/spring-framework-guide/"/>
    <published>2026-05-24T10:10:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Spring-框架全家桶详解：从入门到实战-🌱"><a href="#Spring-框架全家桶详解：从入门到实战-🌱" class="headerlink" title="Spring 框架全家桶详解：从入门到实战 🌱"></a>Spring 框架全家桶详解]]>
    </summary>
    <title>Spring 框架全家桶详解：从入门到实战 🌱</title>
    <updated>2026-05-24T10:15:46.649Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="Web" scheme="https://blog.codenav.top/tags/Web/"/>
    <category term="Servlet" scheme="https://blog.codenav.top/tags/Servlet/"/>
    <category term="HTTP" scheme="https://blog.codenav.top/tags/HTTP/"/>
    <content>
      <![CDATA[<h1 id="Servlet-技术详解：从入门到实战-🦞"><a href="#Servlet-技术详解：从入门到实战-🦞" class="headerlink" title="Servlet 技术详解：从入门到实战 🦞"></a>Servlet 技术详解：从入门到实战 🦞</h1><blockquote><p>Servlet 是 Java Web 开发的基础，是所有 Java Web 框架（如 Spring MVC）的底层原理。理解和掌握 Servlet 的工作机制，对于每一个 Java 后端开发者来说都是必备技能。本文将带你从零开始，系统掌握 Servlet 的核心概念、工作原理、生命周期以及实战开发。💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80servlet-%E6%A6%82%E8%BF%B0%E4%BB%80%E4%B9%88%E6%98%AF-servlet">一、Servlet 概述：什么是 Servlet？</a></li><li><a href="#%E4%BA%8Ctomcat-%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%89%E8%A3%85%E4%B8%8E%E9%85%8D%E7%BD%AE">二、Tomcat 服务器安装与配置</a></li><li><a href="#%E4%B8%89%E7%AC%AC%E4%B8%80%E4%B8%AA-servlet-%E7%A8%8B%E5%BA%8F">三、第一个 Servlet 程序</a></li><li><a href="#%E5%9B%9Bservlet-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">四、Servlet 生命周期</a></li><li><a href="#%E4%BA%94httpservletrequest-%E4%B8%8E-httpservletresponse">五、HttpServletRequest 与 HttpServletResponse</a></li><li><a href="#%E5%85%AD%E4%BC%9A%E8%AF%9D%E7%AE%A1%E7%90%86session-%E4%B8%8E-cookie">六、会话管理：Session 与 Cookie</a></li><li><a href="#%E4%B8%83%E8%BF%87%E6%BB%A4%E5%99%A8%E4%B8%8E%E7%9B%91%E5%90%AC%E5%99%A8">七、过滤器与监听器</a></li><li><a href="#%E5%85%ABservlet-%E6%B3%A8%E8%A7%A3%E5%BC%80%E5%8F%91">八、Servlet 注解开发</a></li><li><a href="#%E4%B9%9Dmvc-%E6%9E%B6%E6%9E%84%E6%A8%A1%E5%BC%8F">九、MVC 架构模式</a></li><li><a href="#%E5%8D%81%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%B8%80%E6%80%BB%E7%BB%93">十一、总结</a></li></ul><hr><h2 id="一、Servlet-概述：什么是-Servlet？"><a href="#一、Servlet-概述：什么是-Servlet？" class="headerlink" title="一、Servlet 概述：什么是 Servlet？"></a>一、Servlet 概述：什么是 Servlet？</h2><h3 id="1-1-Servlet-的定义"><a href="#1-1-Servlet-的定义" class="headerlink" title="1.1 Servlet 的定义"></a>1.1 Servlet 的定义</h3><p>Servlet 是 Sun 公司制定的 <strong>Java Servlet API</strong> 规范，它是一种运行在服务器端的 Java 程序，用于处理客户端（浏览器）的请求并返回响应。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🌐 浏览器"] -->|"HTTP 请求"| B["☕️ Servlet 容器"]    B -->|"调用"| C["📦 Servlet"]    C -->|"处理业务"| D["📋 业务逻辑"]    D -->|"返回响应"| C    C -->|"HTTP 响应"| A        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0</pre></div><h3 id="1-2-Servlet-与-JSP-的关系"><a href="#1-2-Servlet-与-JSP-的关系" class="headerlink" title="1.2 Servlet 与 JSP 的关系"></a>1.2 Servlet 与 JSP 的关系</h3><table><thead><tr><th>对比维度</th><th>Servlet</th><th>JSP</th></tr></thead><tbody><tr><td><strong>本质</strong></td><td>Java 类</td><td>HTML + Java 代码混合</td></tr><tr><td><strong>擅长</strong></td><td>业务逻辑、数据处理</td><td>页面展示、动态内容</td></tr><tr><td><strong>编译</strong></td><td>手动编译</td><td>首次访问时自动编译成 Servlet</td></tr><tr><td><strong>书写</strong></td><td>纯 Java 代码</td><td>HTML 中嵌入 Java 代码</td></tr><tr><td><strong>运行</strong></td><td>必须部署到 Servlet 容器</td><td>运行在 Servlet 容器中</td></tr><tr><td><strong>适用场景</strong></td><td>后端接口、数据处理</td><td>动态页面、模板渲染</td></tr></tbody></table><blockquote><p>💡 <strong>推荐</strong>：Servlet 负责后端逻辑，JSP 负责页面展示，分工明确。现代开发更推荐前后端分离，使用 JSON 作为数据交换格式。</p></blockquote><h3 id="1-3-Servlet-容器的作用"><a href="#1-3-Servlet-容器的作用" class="headerlink" title="1.3 Servlet 容器的作用"></a>1.3 Servlet 容器的作用</h3><p>常见的 Servlet 容器有：<strong>Tomcat、Jetty、Undertow、WebLogic、WebSphere</strong> 等。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["⚙️ Servlet 容器职责"] --> B["🔌 网络通信"]    A --> C["📦 对象生命周期管理"]    A --> D["🛡️ 安全控制"]    A --> E["📝 URL 映射"]    A --> F["🔄 JSP 支持"]        B --> B1["处理 TCP/IP\n协议栈"]        C --> C1["加载 Servlet\n调用构造方法\n管理线程"]        D --> D1["角色权限\n数据安全"]        E --> E1["URL 路径匹配\n请求分发"]        F --> F1["编译 JSP\n转换为 Servlet"]        style A fill:#fff3e0</pre></div><h3 id="1-4-Servlet-版本演进"><a href="#1-4-Servlet-版本演进" class="headerlink" title="1.4 Servlet 版本演进"></a>1.4 Servlet 版本演进</h3><table><thead><tr><th>版本</th><th>发布年份</th><th>主要特性</th></tr></thead><tbody><tr><td><strong>Servlet 2.0</strong></td><td>1997</td><td>最早的 Servlet 规范</td></tr><tr><td><strong>Servlet 2.3&#x2F;2.4</strong></td><td>2001&#x2F;2003</td><td>过滤器的引入</td></tr><tr><td><strong>Servlet 2.5</strong></td><td>2005</td><td>注解支持增强</td></tr><tr><td><strong>Servlet 3.0</strong></td><td>2009</td><td><strong>注解驱动</strong>、异步支持、模块化</td></tr><tr><td><strong>Servlet 3.1</strong></td><td>2013</td><td>非阻塞 I&#x2F;O</td></tr><tr><td><strong>Servlet 4.0</strong></td><td>2017</td><td><strong>HTTP&#x2F;2 支持</strong></td></tr><tr><td><strong>Servlet 5.0+</strong></td><td>2020+</td><td>Jakarta EE 迁移</td></tr></tbody></table><hr><h2 id="二、Tomcat-服务器安装与配置"><a href="#二、Tomcat-服务器安装与配置" class="headerlink" title="二、Tomcat 服务器安装与配置"></a>二、Tomcat 服务器安装与配置</h2><h3 id="2-1-Tomcat-简介"><a href="#2-1-Tomcat-简介" class="headerlink" title="2.1 Tomcat 简介"></a>2.1 Tomcat 简介</h3><p>Apache Tomcat 是 Apache 软件基金会开发的开源 Servlet 容器，是目前最流行的 Java Web 服务器之一。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🐱 Tomcat 架构"] --> B["🖥️ Server"]    B --> C["🌐 Service"]    C --> D["🔌 Connector"]    C --> E["🗂️ Engine"]    E --> F["🛤️ Host"]    F --> G["📁 Context\n（Web 应用）"]    G --> H["🔀 Wrapper\n（Servlet）"]        style A fill:#fff3e0    style G fill:#c8e6c9</pre></div><h3 id="2-2-Tomcat-安装（Windows）"><a href="#2-2-Tomcat-安装（Windows）" class="headerlink" title="2.2 Tomcat 安装（Windows）"></a>2.2 Tomcat 安装（Windows）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 下载 Tomcat</span></span><br><span class="line"><span class="comment"># 下载地址：https://tomcat.apache.org/download-10.cgi</span></span><br><span class="line"><span class="comment"># 选择 10.x 版本（对应 Servlet 5.0+）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 解压到指定目录</span></span><br><span class="line"><span class="comment"># 例如：D:\apache-tomcat-10.1.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 配置环境变量（可选）</span></span><br><span class="line"><span class="comment"># CATALINA_HOME = D:\apache-tomcat-10.1.0</span></span><br><span class="line"><span class="comment"># PATH = %CATALINA_HOME%\bin</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 目录结构说明</span></span><br><span class="line"><span class="comment"># bin/        - 启动/关闭脚本</span></span><br><span class="line"><span class="comment"># conf/       - 配置文件</span></span><br><span class="line"><span class="comment"># lib/        - JAR 包</span></span><br><span class="line"><span class="comment"># logs/       - 日志文件</span></span><br><span class="line"><span class="comment"># webapps/    - 部署的应用</span></span><br><span class="line"><span class="comment"># work/       - 临时文件（JSP 编译）</span></span><br></pre></td></tr></table></figure><h3 id="2-3-Tomcat-目录结构"><a href="#2-3-Tomcat-目录结构" class="headerlink" title="2.3 Tomcat 目录结构"></a>2.3 Tomcat 目录结构</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">apache-tomcat-10.1.0/</span><br><span class="line">├── bin/                    <span class="comment"># 启动脚本</span></span><br><span class="line">│   ├── startup.bat        <span class="comment"># Windows 启动</span></span><br><span class="line">│   ├── startup.sh        <span class="comment"># Linux 启动</span></span><br><span class="line">│   ├── shutdown.bat       <span class="comment"># Windows 关闭</span></span><br><span class="line">│   └── version.sh         <span class="comment"># 查看版本</span></span><br><span class="line">│</span><br><span class="line">├── conf/                  <span class="comment"># 配置文件</span></span><br><span class="line">│   ├── server.xml        <span class="comment"># 服务器配置</span></span><br><span class="line">│   ├── web.xml           <span class="comment"># Web 应用配置</span></span><br><span class="line">│   ├── context.xml       <span class="comment"># 上下文配置</span></span><br><span class="line">│   └── tomcat-users.xml  <span class="comment"># 用户认证</span></span><br><span class="line">│</span><br><span class="line">├── lib/                   <span class="comment"># Tomcat 依赖 JAR</span></span><br><span class="line">│</span><br><span class="line">├── logs/                  <span class="comment"># 日志目录</span></span><br><span class="line">│   ├── catalina.out       <span class="comment"># 启动日志</span></span><br><span class="line">│   └── localhost.log      <span class="comment"># 应用日志</span></span><br><span class="line">│</span><br><span class="line">├── webapps/               <span class="comment"># Web 应用目录</span></span><br><span class="line">│   ├── docs/             <span class="comment"># Tomcat 文档</span></span><br><span class="line">│   ├── examples/         <span class="comment"># 示例应用</span></span><br><span class="line">│   └── manager/          <span class="comment"># 管理应用</span></span><br><span class="line">│</span><br><span class="line">└── work/                  <span class="comment"># 工作目录</span></span><br><span class="line">    └── Catalina/          <span class="comment"># JSP 编译目录</span></span><br></pre></td></tr></table></figure><h3 id="2-4-启动-Tomcat"><a href="#2-4-启动-Tomcat" class="headerlink" title="2.4 启动 Tomcat"></a>2.4 启动 Tomcat</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Windows 启动</span></span><br><span class="line"><span class="comment"># 方式一：双击 bin/startup.bat</span></span><br><span class="line"><span class="comment"># 方式二：命令行</span></span><br><span class="line"><span class="built_in">cd</span> D:\apache-tomcat-10.1.0\bin</span><br><span class="line">startup.bat</span><br><span class="line"></span><br><span class="line"><span class="comment"># Linux 启动</span></span><br><span class="line">./bin/startup.sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证启动成功</span></span><br><span class="line"><span class="comment"># 浏览器访问：http://localhost:8080</span></span><br><span class="line"><span class="comment"># 看到 Tomcat 主页说明启动成功</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭 Tomcat</span></span><br><span class="line"><span class="comment"># Windows</span></span><br><span class="line">shutdown.bat</span><br><span class="line"><span class="comment"># Linux</span></span><br><span class="line">./bin/shutdown.sh</span><br></pre></td></tr></table></figure><h3 id="2-5-修改端口号"><a href="#2-5-修改端口号" class="headerlink" title="2.5 修改端口号"></a>2.5 修改端口号</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- conf/server.xml --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">Server</span> <span class="attr">port</span>=<span class="string">&quot;8005&quot;</span> <span class="attr">shutdown</span>=<span class="string">&quot;SHUTDOWN&quot;</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 修改 Connector 的 port 属性即可 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">Service</span> <span class="attr">name</span>=<span class="string">&quot;Catalina&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Connector</span> <span class="attr">port</span>=<span class="string">&quot;8080&quot;</span> <span class="attr">protocol</span>=<span class="string">&quot;HTTP/1.1&quot;</span></span></span><br><span class="line"><span class="tag">                   <span class="attr">connectionTimeout</span>=<span class="string">&quot;20000&quot;</span></span></span><br><span class="line"><span class="tag">                   <span class="attr">redirectPort</span>=<span class="string">&quot;8443&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">Service</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">Server</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="三、第一个-Servlet-程序"><a href="#三、第一个-Servlet-程序" class="headerlink" title="三、第一个 Servlet 程序"></a>三、第一个 Servlet 程序</h2><h3 id="3-1-项目结构设计"><a href="#3-1-项目结构设计" class="headerlink" title="3.1 项目结构设计"></a>3.1 项目结构设计</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📁 WebApp 项目结构"] --> B["📁 src/main/java"]    A --> C["📁 src/main/webapp"]    A --> D["📁 src/main/resources"]        B --> E["📁 com.example.servlet"]    C --> F["📄 index.jsp"]    C --> G["📄 WEB-INF/"]    G --> H["📄 web.xml"]    G --> I["📄 lib/"]        style A fill:#e3f2fd    style B fill:#c8e6c9</pre></div><h3 id="3-2-Maven-项目配置"><a href="#3-2-Maven-项目配置" class="headerlink" title="3.2 Maven 项目配置"></a>3.2 Maven 项目配置</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- pom.xml --&gt;</span></span><br><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0</span></span></span><br><span class="line"><span class="string"><span class="tag">         http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.example<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>servlet-demo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>war<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>17<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>17<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- Servlet API（provided：运行时由容器提供） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>jakarta.servlet<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jakarta.servlet-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>6.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- JSP API --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>jakarta.servlet.jsp<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jakarta.servlet.jsp-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- JSTL 标签库 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.glassfish.web<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jakarta.servlet.jsp.jstl<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">finalName</span>&gt;</span>servlet-demo<span class="tag">&lt;/<span class="name">finalName</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- Tomcat 插件（方便运行） --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.tomcat.maven<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>tomcat-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0-r1655215<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">port</span>&gt;</span>8080<span class="tag">&lt;/<span class="name">port</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">path</span>&gt;</span>/<span class="tag">&lt;/<span class="name">path</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-3-创建-Servlet-类"><a href="#3-3-创建-Servlet-类" class="headerlink" title="3.3 创建 Servlet 类"></a>3.3 创建 Servlet 类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.servlet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.WebServlet;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.http.HttpServlet;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.http.HttpServletResponse;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintWriter;</span><br><span class="line"><span class="keyword">import</span> java.time.LocalDateTime;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 第一个 Servlet 程序</span></span><br><span class="line"><span class="comment"> * 访问地址：http://localhost:8080/first-servlet</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/first-servlet&quot;)</span>  <span class="comment">// 注解配置 URL 映射</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FirstServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                       HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置响应内容类型和字符编码</span></span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.setCharacterEncoding(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取输出流</span></span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 输出 HTML 内容</span></span><br><span class="line">        out.println(<span class="string">&quot;&lt;!DOCTYPE html&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;html&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;head&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;meta charset=&#x27;UTF-8&#x27;&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;title&gt;第一个 Servlet&lt;/title&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;/head&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;body&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;h1&gt;🎉 Hello, Servlet! 🦞&lt;/h1&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;p&gt;当前时间：&quot;</span> + LocalDateTime.now() + <span class="string">&quot;&lt;/p&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;p&gt;当前会话 ID：&quot;</span> + request.getSession().getId() + <span class="string">&quot;&lt;/p&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;p&gt;Servlet 版本：jakarta.servlet-api 6.0&lt;/p&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;/body&gt;&quot;</span>);</span><br><span class="line">        out.println(<span class="string">&quot;&lt;/html&gt;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-传统-XML-配置方式"><a href="#3-4-传统-XML-配置方式" class="headerlink" title="3.4 传统 XML 配置方式"></a>3.4 传统 XML 配置方式</h3><p>如果使用 Servlet 3.0 之前的版本，或者项目不使用注解，需要在 <code>web.xml</code> 中配置：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- src/main/webapp/WEB-INF/web.xml --&gt;</span></span><br><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">web-app</span> <span class="attr">xmlns</span>=<span class="string">&quot;https://jakarta.ee/xml/ns/jakartaee&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;https://jakarta.ee/xml/ns/jakartaee</span></span></span><br><span class="line"><span class="string"><span class="tag">         https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">version</span>=<span class="string">&quot;6.0&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">display-name</span>&gt;</span>Servlet Demo Application<span class="tag">&lt;/<span class="name">display-name</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置 Servlet --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- Servlet 名称 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>HelloServlet<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- Servlet 类的全限定名 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span>com.example.servlet.HelloServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 启动顺序（可选） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">load-on-startup</span>&gt;</span>1<span class="tag">&lt;/<span class="name">load-on-startup</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置 Servlet 映射 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>HelloServlet<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 访问 URL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/hello<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 欢迎页面 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">welcome-file-list</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">welcome-file</span>&gt;</span>index.jsp<span class="tag">&lt;/<span class="name">welcome-file</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">welcome-file-list</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">web-app</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-5-运行项目"><a href="#3-5-运行项目" class="headerlink" title="3.5 运行项目"></a>3.5 运行项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方式一：使用 Maven 插件运行</span></span><br><span class="line">mvn tomcat:run</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：手动部署</span></span><br><span class="line"><span class="comment"># 1. 打包 WAR 文件</span></span><br><span class="line">mvn package</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 将 target/servlet-demo.war 复制到 Tomcat 的 webapps 目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 启动 Tomcat</span></span><br><span class="line"><span class="comment"># 访问地址：http://localhost:8080/servlet-demo/first-servlet</span></span><br></pre></td></tr></table></figure><hr><h2 id="四、Servlet-生命周期"><a href="#四、Servlet-生命周期" class="headerlink" title="四、Servlet 生命周期"></a>四、Servlet 生命周期</h2><h3 id="4-1-生命周期四个阶段"><a href="#4-1-生命周期四个阶段" class="headerlink" title="4.1 生命周期四个阶段"></a>4.1 生命周期四个阶段</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔄 Servlet 生命周期"] --> B["1️⃣ 实例化"]    B --> C["2️⃣ 初始化 init()"]    C --> D["3️⃣ 服务 doGet/doPost"]    D --> E["4️⃣ 销毁 destroy()"]        B -.->|"首次请求时"| B    C -.->|"只执行一次"| C    D -.->|"每次请求都执行"| D    E -.->|"应用卸载或重启时"| E        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#f8bbd0</pre></div><table><thead><tr><th>阶段</th><th>执行时机</th><th>执行次数</th><th>说明</th></tr></thead><tbody><tr><td><strong>实例化</strong></td><td>首次请求时（或容器启动时</td><td>1 次</td><td>Servlet 容器创建 Servlet 实例</td></tr><tr><td><strong>初始化</strong></td><td>实例化后</td><td>1 次</td><td>调用 <code>init()</code> 方法，可获取初始化参数</td></tr><tr><td><strong>服务</strong></td><td>每次请求</td><td>多次</td><td>调用 <code>service()</code> → 分发到 <code>doGet/doPost</code></td></tr><tr><td><strong>销毁</strong></td><td>应用卸载&#x2F;容器关闭</td><td>1 次</td><td>调用 <code>destroy()</code>，释放资源</td></tr></tbody></table><h3 id="4-2-生命周期代码演示"><a href="#4-2-生命周期代码演示" class="headerlink" title="4.2 生命周期代码演示"></a>4.2 生命周期代码演示</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.servlet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.WebServlet;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.http.HttpServlet;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Servlet 生命周期演示</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/lifecycle&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LifecycleServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 计数器：统计请求次数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">requestCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LifecycleServlet</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">        System.out.println(<span class="string">&quot;📦 1. LifecycleServlet 构造方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">        <span class="built_in">super</span>.init();</span><br><span class="line">        System.out.println(<span class="string">&quot;🔧 2. init() 方法被调用，Servlet 初始化完成&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">service</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                          HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        requestCount++;</span><br><span class="line">        System.out.println(<span class="string">&quot;🔄 3. service() 方法被调用，第 &quot;</span> + requestCount + <span class="string">&quot; 次请求&quot;</span>);</span><br><span class="line">        <span class="built_in">super</span>.service(request, response);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                        HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;📝 4. doGet() 方法被调用&quot;</span>);</span><br><span class="line">        response.getWriter().write(</span><br><span class="line">            <span class="string">&quot;&lt;h1&gt;Servlet 生命周期演示&lt;/h1&gt;&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&lt;p&gt;请求次数：&quot;</span> + requestCount + <span class="string">&quot;&lt;/p&gt;&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&lt;p&gt;当前会话 ID：&quot;</span> + request.getSession().getId() + <span class="string">&quot;&lt;/p&gt;&quot;</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">destroy</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.destroy();</span><br><span class="line">        System.out.println(<span class="string">&quot;🗑️ 5. destroy() 方法被调用，Servlet 即将被销毁&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;总共处理了 &quot;</span> + requestCount + <span class="string">&quot; 次请求&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-3-init-方法详解"><a href="#4-3-init-方法详解" class="headerlink" title="4.3 init() 方法详解"></a>4.3 init() 方法详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * init() 方法的重写方式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式一：重写无参 init()</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">    <span class="comment">// 获取初始化参数（web.xml 中配置）</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">encoding</span> <span class="operator">=</span> getServletConfig().getInitParameter(<span class="string">&quot;encoding&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;编码格式：&quot;</span> + encoding);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二：重写带 ServletConfig 的 init()</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">(ServletConfig config)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">    <span class="built_in">super</span>.init(config);</span><br><span class="line">    <span class="comment">// 可以在调用 super.init(config) 之后使用 config</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式三：使用 @PostConstruct 注解（替代 init）</span></span><br><span class="line"><span class="keyword">import</span> jakarta.annotation.PostConstruct;</span><br><span class="line"></span><br><span class="line"><span class="meta">@PostConstruct</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">initMethod</span><span class="params">()</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;初始化方法被调用&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、HttpServletRequest-与-HttpServletResponse"><a href="#五、HttpServletRequest-与-HttpServletResponse" class="headerlink" title="五、HttpServletRequest 与 HttpServletResponse"></a>五、HttpServletRequest 与 HttpServletResponse</h2><h3 id="5-1-HttpServletRequest-对象"><a href="#5-1-HttpServletRequest-对象" class="headerlink" title="5.1 HttpServletRequest 对象"></a>5.1 HttpServletRequest 对象</h3><p><code>HttpServletRequest</code> 封装了客户端的 HTTP 请求信息：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HttpServletRequest 常用方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/request-demo&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RequestDemoServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 获取请求信息 ==========</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求方法</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">method</span> <span class="operator">=</span> request.getMethod();  <span class="comment">// GET / POST</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求 URL</span></span><br><span class="line">        <span class="type">StringBuffer</span> <span class="variable">url</span> <span class="operator">=</span> request.getRequestURL();  <span class="comment">// http://localhost:8080/demo</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">uri</span> <span class="operator">=</span> request.getRequestURI();         <span class="comment">// /demo</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求参数</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取多个值（如 checkbox）</span></span><br><span class="line">        String[] hobbies = request.getParameterValues(<span class="string">&quot;hobby&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求头</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">userAgent</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;User-Agent&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">contentType</span> <span class="operator">=</span> request.getContentType();</span><br><span class="line">        <span class="type">int</span> <span class="variable">contentLength</span> <span class="operator">=</span> request.getContentLength();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取 Session</span></span><br><span class="line">        javax.servlet.http.<span class="type">HttpSession</span> <span class="variable">session</span> <span class="operator">=</span> request.getSession();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取 Cookie</span></span><br><span class="line">        Cookie[] cookies = request.getCookies();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求体（POST 请求体）</span></span><br><span class="line">        <span class="comment">// 需要先设置编码，否则中文乱码</span></span><br><span class="line">        request.setCharacterEncoding(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        <span class="type">BufferedReader</span> <span class="variable">reader</span> <span class="operator">=</span> request.getReader();</span><br><span class="line">        String line;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">body</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="keyword">while</span> ((line = reader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">            body.append(line);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 存储请求域属性 ==========</span></span><br><span class="line">        <span class="comment">// 用于在同一次请求中传递数据</span></span><br><span class="line">        request.setAttribute(<span class="string">&quot;user&quot;</span>, <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;张三&quot;</span>, <span class="number">25</span>));</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求域属性</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> (User) request.getAttribute(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取转发对象</span></span><br><span class="line">        <span class="type">RequestDispatcher</span> <span class="variable">dispatcher</span> <span class="operator">=</span> request.getRequestDispatcher(<span class="string">&quot;/other&quot;</span>);</span><br><span class="line">        <span class="comment">// dispatcher.forward(request, response);  // 转发</span></span><br><span class="line">        <span class="comment">// dispatcher.include(request, response);    // 包含</span></span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;请求方法：&quot;</span> + method + <span class="string">&quot;&lt;br/&gt;&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;请求 URL：&quot;</span> + url);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-2-HttpServletResponse-对象"><a href="#5-2-HttpServletResponse-对象" class="headerlink" title="5.2 HttpServletResponse 对象"></a>5.2 HttpServletResponse 对象</h3><p><code>HttpServletResponse</code> 用于构建 HTTP 响应：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HttpServletResponse 常用方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/response-demo&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResponseDemoServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 设置响应 ==========</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置响应内容类型</span></span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        <span class="comment">// 其他常用类型：</span></span><br><span class="line">        <span class="comment">// application/json;charset=UTF-8</span></span><br><span class="line">        <span class="comment">// text/plain;charset=UTF-8</span></span><br><span class="line">        <span class="comment">// image/png</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置响应状态码</span></span><br><span class="line">        response.setStatus(<span class="number">200</span>);  <span class="comment">// 正常</span></span><br><span class="line">        <span class="comment">// response.setStatus(404); // 未找到</span></span><br><span class="line">        <span class="comment">// response.setStatus(500); // 服务器内部错误</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置响应头</span></span><br><span class="line">        response.setHeader(<span class="string">&quot;Cache-Control&quot;</span>, <span class="string">&quot;no-cache&quot;</span>);</span><br><span class="line">        response.setHeader(<span class="string">&quot;X-Custom-Header&quot;</span>, <span class="string">&quot;custom-value&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 获取输出流 ==========</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 字符流（适合文本内容）</span></span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">writer</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        writer.write(<span class="string">&quot;Hello, World!&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 字节流（适合二进制内容，如图片）</span></span><br><span class="line">        <span class="comment">// ServletOutputStream out = response.getOutputStream();</span></span><br><span class="line">        <span class="comment">// out.write(imageBytes);</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 重定向 ==========</span></span><br><span class="line">        <span class="comment">// 浏览器请求 /redirect-demo，服务器告诉浏览器跳转到 /target</span></span><br><span class="line">        response.sendRedirect(request.getContextPath() + <span class="string">&quot;/target&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 设置 Cookie ==========</span></span><br><span class="line">        <span class="type">Cookie</span> <span class="variable">cookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;username&quot;</span>, <span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        cookie.setMaxAge(<span class="number">3600</span>);           <span class="comment">// 有效期（秒）</span></span><br><span class="line">        cookie.setPath(<span class="string">&quot;/&quot;</span>);              <span class="comment">// 有效路径</span></span><br><span class="line">        cookie.setHttpOnly(<span class="literal">true</span>);         <span class="comment">// 禁止 JavaScript 访问</span></span><br><span class="line">        response.addCookie(cookie);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-3-请求参数中文乱码解决"><a href="#5-3-请求参数中文乱码解决" class="headerlink" title="5.3 请求参数中文乱码解决"></a>5.3 请求参数中文乱码解决</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 中文乱码问题解决方案</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/encoding-demo&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EncodingDemoServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// GET 请求参数乱码：在 Tomcat server.xml 中配置</span></span><br><span class="line">        <span class="comment">// &lt;Connector port=&quot;8080&quot; URIEncoding=&quot;UTF-8&quot; /&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 或者使用 URLEncoder/URLDecoder 编解码</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        name = <span class="keyword">new</span> <span class="title class_">String</span>(name.getBytes(<span class="string">&quot;ISO-8859-1&quot;</span>), <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;GET 参数：&quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doPost</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                          HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// POST 请求参数乱码：设置请求编码</span></span><br><span class="line">        request.setCharacterEncoding(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;POST 参数：username=&quot;</span> + username + <span class="string">&quot;, password=&quot;</span> + password);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、会话管理：Session-与-Cookie"><a href="#六、会话管理：Session-与-Cookie" class="headerlink" title="六、会话管理：Session 与 Cookie"></a>六、会话管理：Session 与 Cookie</h2><h3 id="6-1-会话管理概念"><a href="#6-1-会话管理概念" class="headerlink" title="6.1 会话管理概念"></a>6.1 会话管理概念</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["👤 用户浏览器"] -->|"第一次请求\n无 Cookie"| B["🖥️ 服务器"]    B -->|"创建 Session\n返回 JSESSIONID"| C["📦 Session 对象"]    C -->|"Cookie: JSESSIONID"| A        A -->|"第二次请求\n携带 Cookie"| B    B -->|"查找 Session"| C        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0</pre></div><h3 id="6-2-Cookie-使用示例"><a href="#6-2-Cookie-使用示例" class="headerlink" title="6.2 Cookie 使用示例"></a>6.2 Cookie 使用示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Cookie 操作示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/cookie-demo&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CookieDemoServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 创建 Cookie ==========</span></span><br><span class="line">        <span class="type">Cookie</span> <span class="variable">usernameCookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;username&quot;</span>, <span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        usernameCookie.setMaxAge(<span class="number">3600</span>);    <span class="comment">// 有效期 1 小时</span></span><br><span class="line">        usernameCookie.setPath(<span class="string">&quot;/&quot;</span>);       <span class="comment">// 整个应用有效</span></span><br><span class="line">        usernameCookie.setHttpOnly(<span class="literal">true</span>);  <span class="comment">// 防止 XSS 攻击</span></span><br><span class="line">        response.addCookie(usernameCookie);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 读取 Cookie ==========</span></span><br><span class="line">        Cookie[] cookies = request.getCookies();</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (cookies != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Cookie cookie : cookies) &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="string">&quot;username&quot;</span>.equals(cookie.getName())) &#123;</span><br><span class="line">                    username = cookie.getValue();</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 删除 Cookie ==========</span></span><br><span class="line">        <span class="type">Cookie</span> <span class="variable">deleteCookie</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cookie</span>(<span class="string">&quot;username&quot;</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line">        deleteCookie.setMaxAge(<span class="number">0</span>);   <span class="comment">// 设置为 0 表示删除</span></span><br><span class="line">        deleteCookie.setPath(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">        response.addCookie(deleteCookie);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(</span><br><span class="line">            <span class="string">&quot;&lt;h1&gt;Cookie 演示 �饼干&lt;/h1&gt;&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&lt;p&gt;用户名：&quot;</span> + (username != <span class="literal">null</span> ? username : <span class="string">&quot;未设置&quot;</span>) + <span class="string">&quot;&lt;/p&gt;&quot;</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-Session-使用示例"><a href="#6-3-Session-使用示例" class="headerlink" title="6.3 Session 使用示例"></a>6.3 Session 使用示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HttpSession 操作示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/session-demo&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SessionDemoServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                        HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 获取 Session ==========</span></span><br><span class="line">        <span class="comment">// request.getSession() 等价于 request.getSession(true)</span></span><br><span class="line">        <span class="comment">// true：如果不存在则创建新的 Session</span></span><br><span class="line">        <span class="comment">// false：如果不存在则返回 null</span></span><br><span class="line">        javax.servlet.http.<span class="type">HttpSession</span> <span class="variable">session</span> <span class="operator">=</span> request.getSession();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 存储数据 ==========</span></span><br><span class="line">        session.setAttribute(<span class="string">&quot;userId&quot;</span>, <span class="number">1001L</span>);</span><br><span class="line">        session.setAttribute(<span class="string">&quot;username&quot;</span>, <span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 读取数据 ==========</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> (Long) session.getAttribute(<span class="string">&quot;userId&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> (String) session.getAttribute(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 移除数据 ==========</span></span><br><span class="line">        session.removeAttribute(<span class="string">&quot;userId&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== Session 配置 ==========</span></span><br><span class="line">        session.setMaxInactiveInterval(<span class="number">1800</span>);  <span class="comment">// 30 分钟无活动则过期</span></span><br><span class="line">        session.getMaxInactiveInterval();       <span class="comment">// 获取超时时间</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 使 Session 失效 ==========</span></span><br><span class="line">        <span class="comment">// session.invalidate();  // 销毁整个 Session</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 获取 Session 信息 ==========</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">sessionId</span> <span class="operator">=</span> session.getId();            <span class="comment">// Session ID</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">creationTime</span> <span class="operator">=</span> session.getCreationTime(); <span class="comment">// 创建时间</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">lastAccessedTime</span> <span class="operator">=</span> session.getLastAccessedTime(); <span class="comment">// 最后访问时间</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isNew</span> <span class="operator">=</span> session.isNew();               <span class="comment">// 是否新建</span></span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(</span><br><span class="line">            <span class="string">&quot;&lt;h1&gt;Session 演示 🗃️&lt;/h1&gt;&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&lt;p&gt;Session ID：&quot;</span> + sessionId + <span class="string">&quot;&lt;/p&gt;&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&lt;p&gt;用户名：&quot;</span> + username + <span class="string">&quot;&lt;/p&gt;&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&lt;p&gt;是否新 Session：&quot;</span> + isNew + <span class="string">&quot;&lt;/p&gt;&quot;</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-4-Session-失效的几种情况"><a href="#6-4-Session-失效的几种情况" class="headerlink" title="6.4 Session 失效的几种情况"></a>6.4 Session 失效的几种情况</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔚 Session 失效情况"] --> B["⏰ 超时过期"]    A --> C["👤 用户关闭浏览器"]    A --> D["🗑️ 手动 invalidate()"]    A --> E["🛑 服务器停止"]        B --> B1["默认 30 分钟\n无活动"]    C --> C1["Cookie 被清除\nJSESSIONID 丢失"]    D --> D1["主动注销登录\n退出操作"]    E --> E1["应用卸载\n容器重启"]        style A fill:#fff3e0</pre></div><h3 id="6-5-Cookie-vs-Session-对比"><a href="#6-5-Cookie-vs-Session-对比" class="headerlink" title="6.5 Cookie vs Session 对比"></a>6.5 Cookie vs Session 对比</h3><table><thead><tr><th>对比维度</th><th>Cookie</th><th>Session</th></tr></thead><tbody><tr><td><strong>存储位置</strong></td><td>浏览器本地</td><td>服务器内存</td></tr><tr><td><strong>存储容量</strong></td><td>每个 Cookie 最大 4KB</td><td>理论上无限制</td></tr><tr><td><strong>安全性</strong></td><td>较低（可被禁用&#x2F;伪造）</td><td>较高（数据在服务器端）</td></tr><tr><td><strong>性能</strong></td><td>不占用服务器资源</td><td>占用服务器内存</td></tr><tr><td><strong>跨会话</strong></td><td>可设置跨会话持久化</td><td>仅当前会话有效</td></tr><tr><td><strong>适用场景</strong></td><td>自动登录、记住我</td><td>登录状态、购物车</td></tr></tbody></table><hr><h2 id="七、过滤器与监听器"><a href="#七、过滤器与监听器" class="headerlink" title="七、过滤器与监听器"></a>七、过滤器与监听器</h2><h3 id="7-1-过滤器（Filter）概述"><a href="#7-1-过滤器（Filter）概述" class="headerlink" title="7.1 过滤器（Filter）概述"></a>7.1 过滤器（Filter）概述</h3><p>过滤器是 Servlet 2.3 规范引入的组件，用于在请求到达 Servlet 之前或响应返回之前对请求&#x2F;响应进行预处理。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔍 过滤器链"] --> B["请求"]     B --> C["Filter 1"]    C --> D["Filter 2"]    D --> E["Servlet"]    E --> F["业务逻辑"]    F --> G["Servlet"]    G --> H["Filter 2"]    H --> I["Filter 1"]    I --> J["响应"]        style C fill:#c8e6c9    style D fill:#c8e6c9    style E fill:#e3f2fd    style F fill:#fff3e0</pre></div><h3 id="7-2-过滤器示例"><a href="#7-2-过滤器示例" class="headerlink" title="7.2 过滤器示例"></a>7.2 过滤器示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.filter;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.*;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.WebFilter;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 字符编码过滤器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebFilter(&quot;/*&quot;)</span>  <span class="comment">// 拦截所有请求</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EncodingFilter</span> <span class="keyword">implements</span> <span class="title class_">Filter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">encoding</span> <span class="operator">=</span> <span class="string">&quot;UTF-8&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">(FilterConfig filterConfig)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">        <span class="comment">// 获取初始化参数</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">initEncoding</span> <span class="operator">=</span> filterConfig.getInitParameter(<span class="string">&quot;encoding&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (initEncoding != <span class="literal">null</span> &amp;&amp; !initEncoding.isEmpty()) &#123;</span><br><span class="line">            encoding = initEncoding;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ EncodingFilter 初始化完成，编码：&quot;</span> + encoding);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doFilter</span><span class="params">(ServletRequest request, </span></span><br><span class="line"><span class="params">                        ServletResponse response,</span></span><br><span class="line"><span class="params">                        FilterChain chain)</span> </span><br><span class="line">            <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 请求预处理</span></span><br><span class="line">        request.setCharacterEncoding(encoding);</span><br><span class="line">        response.setCharacterEncoding(encoding);</span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html;charset=&quot;</span> + encoding);</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;🔍 请求被 EncodingFilter 拦截&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 放行请求</span></span><br><span class="line">        chain.doFilter(request, response);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 响应后处理</span></span><br><span class="line">        System.out.println(<span class="string">&quot;🔍 响应被 EncodingFilter 处理完成&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">destroy</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;🗑️ EncodingFilter 被销毁&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-3-用户登录过滤器"><a href="#7-3-用户登录过滤器" class="headerlink" title="7.3 用户登录过滤器"></a>7.3 用户登录过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 登录验证过滤器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebFilter(urlPatterns = &#123;&quot;/admin/*&quot;, &quot;/api/*&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginFilter</span> <span class="keyword">implements</span> <span class="title class_">Filter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doFilter</span><span class="params">(ServletRequest request, </span></span><br><span class="line"><span class="params">                        ServletResponse response,</span></span><br><span class="line"><span class="params">                        FilterChain chain)</span> </span><br><span class="line">            <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">HttpServletRequest</span> <span class="variable">httpRequest</span> <span class="operator">=</span> (HttpServletRequest) request;</span><br><span class="line">        <span class="type">HttpServletResponse</span> <span class="variable">httpResponse</span> <span class="operator">=</span> (HttpServletResponse) response;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取 Session 中的用户信息</span></span><br><span class="line">        javax.servlet.http.<span class="type">HttpSession</span> <span class="variable">session</span> <span class="operator">=</span> httpRequest.getSession(<span class="literal">false</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">user</span> <span class="operator">=</span> session != <span class="literal">null</span> ? session.getAttribute(<span class="string">&quot;user&quot;</span>) : <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (user != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 已登录，放行</span></span><br><span class="line">            chain.doFilter(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 未登录，重定向到登录页</span></span><br><span class="line">            httpResponse.sendRedirect(httpRequest.getContextPath() + <span class="string">&quot;/login&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-4-监听器（Listener）概述"><a href="#7-4-监听器（Listener）概述" class="headerlink" title="7.4 监听器（Listener）概述"></a>7.4 监听器（Listener）概述</h3><table><thead><tr><th>监听器类型</th><th>接口</th><th>触发时机</th></tr></thead><tbody><tr><td><strong>ServletContext 监听器</strong></td><td>ServletContextListener</td><td>应用启动&#x2F;停止</td></tr><tr><td><strong>Session 监听器</strong></td><td>HttpSessionListener</td><td>Session 创建&#x2F;销毁</td></tr><tr><td><strong>请求监听器</strong></td><td>ServletRequestListener</td><td>请求创建&#x2F;销毁</td></tr><tr><td><strong>属性监听器</strong></td><td>ServletContextAttributeListener 等</td><td>属性添加&#x2F;删除&#x2F;替换</td></tr></tbody></table><h3 id="7-5-监听器示例"><a href="#7-5-监听器示例" class="headerlink" title="7.5 监听器示例"></a>7.5 监听器示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.listener;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.*;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.WebListener;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 应用启动监听器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebListener</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ApplicationListener</span> <span class="keyword">implements</span> <span class="title class_">ServletContextListener</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">contextInitialized</span><span class="params">(ServletContextEvent sce)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;🚀 应用启动：Spring Boot 初始化中...&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 可以在这里加载配置文件、初始化数据库连接池等</span></span><br><span class="line">        <span class="type">ServletContext</span> <span class="variable">context</span> <span class="operator">=</span> sce.getServletContext();</span><br><span class="line">        context.setAttribute(<span class="string">&quot;appName&quot;</span>, <span class="string">&quot;Servlet Demo&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取初始化参数</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">adminEmail</span> <span class="operator">=</span> context.getInitParameter(<span class="string">&quot;adminEmail&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;管理员邮箱：&quot;</span> + adminEmail);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">contextDestroyed</span><span class="params">(ServletContextEvent sce)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;🛑 应用停止：资源释放中...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Session 监听器：统计在线人数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebListener</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SessionListener</span> <span class="keyword">implements</span> <span class="title class_">HttpSessionListener</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 存储在线人数（需要线程安全）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">onlineCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sessionCreated</span><span class="params">(HttpSessionEvent se)</span> &#123;</span><br><span class="line">        onlineCount++;</span><br><span class="line">        System.out.println(<span class="string">&quot;👤 Session 创建，当前在线人数：&quot;</span> + onlineCount);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sessionDestroyed</span><span class="params">(HttpSessionEvent se)</span> &#123;</span><br><span class="line">        onlineCount--;</span><br><span class="line">        System.out.println(<span class="string">&quot;👋 Session 销毁，当前在线人数：&quot;</span> + onlineCount);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getOnlineCount</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> onlineCount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、Servlet-注解开发"><a href="#八、Servlet-注解开发" class="headerlink" title="八、Servlet 注解开发"></a>八、Servlet 注解开发</h2><h3 id="8-1-常用注解一览"><a href="#8-1-常用注解一览" class="headerlink" title="8.1 常用注解一览"></a>8.1 常用注解一览</h3><table><thead><tr><th>注解</th><th>作用</th><th>示例</th></tr></thead><tbody><tr><td><strong>@WebServlet</strong></td><td>配置 Servlet 的 URL 映射</td><td><code>@WebServlet(&quot;/user&quot;)</code></td></tr><tr><td><strong>@WebFilter</strong></td><td>配置过滤器</td><td><code>@WebFilter(&quot;/*&quot;)</code></td></tr><tr><td><strong>@WebListener</strong></td><td>配置监听器</td><td><code>@WebListener</code></td></tr><tr><td><strong>@WebInitParam</strong></td><td>配置初始化参数</td><td><code>@WebInitParam(name=&quot;encoding&quot;, value=&quot;UTF-8&quot;)</code></td></tr><tr><td><strong>@MultipartConfig</strong></td><td>配置文件上传</td><td><code>@MultipartConfig</code></td></tr></tbody></table><h3 id="8-2-完整注解示例"><a href="#8-2-完整注解示例" class="headerlink" title="8.2 完整注解示例"></a>8.2 完整注解示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.servlet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.MultipartConfig;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.WebInitParam;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.annotation.WebServlet;</span><br><span class="line"><span class="keyword">import</span> jakarta.servlet.http.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintWriter;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 完整的注解配置示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(</span></span><br><span class="line"><span class="meta">    name = &quot;UserServlet&quot;,                           // Servlet 名称</span></span><br><span class="line"><span class="meta">    urlPatterns = &#123;&quot;/api/user&quot;, &quot;/api/users&quot;&#125;,      // 多个 URL 映射</span></span><br><span class="line"><span class="meta">    initParams = &#123;                                  // 初始化参数</span></span><br><span class="line"><span class="meta">        @WebInitParam(name = &quot;encoding&quot;, value = &quot;UTF-8&quot;),</span></span><br><span class="line"><span class="meta">        @WebInitParam(name = &quot;pageSize&quot;, value = &quot;20&quot;)</span></span><br><span class="line"><span class="meta">    &#125;,</span></span><br><span class="line"><span class="meta">    loadOnStartup = 1,                              // 启动时加载</span></span><br><span class="line"><span class="meta">    description = &quot;用户管理 Servlet&quot;                 // 描述</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@MultipartConfig(                                   // 文件上传配置</span></span><br><span class="line"><span class="meta">    fileSizeThreshold = 1024 * 1024,               // 超过 1MB 则写入磁盘</span></span><br><span class="line"><span class="meta">    maxFileSize = 10 * 1024 * 1024,                // 最大文件大小 10MB</span></span><br><span class="line"><span class="meta">    maxRequestSize = 50 * 1024 * 1024              // 最大请求大小 50MB</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String encoding;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> pageSize;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">        <span class="comment">// 获取初始化参数</span></span><br><span class="line">        encoding = getServletConfig().getInitParameter(<span class="string">&quot;encoding&quot;</span>);</span><br><span class="line">        pageSize = Integer.parseInt(getServletConfig().getInitParameter(<span class="string">&quot;pageSize&quot;</span>));</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ UserServlet 初始化：encoding=&quot;</span> + encoding + <span class="string">&quot;, pageSize=&quot;</span> + pageSize);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">action</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;action&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;list&quot;</span>.equals(action)) &#123;</span><br><span class="line">            listUsers(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;detail&quot;</span>.equals(action)) &#123;</span><br><span class="line">            getUserDetail(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            listUsers(request, response);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doPost</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        request.setCharacterEncoding(encoding);</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">action</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;action&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;create&quot;</span>.equals(action)) &#123;</span><br><span class="line">            createUser(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;update&quot;</span>.equals(action)) &#123;</span><br><span class="line">            updateUser(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;delete&quot;</span>.equals(action)) &#123;</span><br><span class="line">            deleteUser(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;upload&quot;</span>.equals(action)) &#123;</span><br><span class="line">            uploadAvatar(request, response);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">listUsers</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                          HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=&quot;</span> + encoding);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        out.println(<span class="string">&quot;&#123;\&quot;users\&quot;:[&#123;\&quot;id\&quot;:1,\&quot;name\&quot;:\&quot;张三\&quot;&#125;,&#123;\&quot;id\&quot;:2,\&quot;name\&quot;:\&quot;李四\&quot;&#125;]&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">getUserDetail</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                              HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=&quot;</span> + encoding);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        out.println(<span class="string">&quot;&#123;\&quot;id\&quot;:&quot;</span> + id + <span class="string">&quot;,\&quot;name\&quot;:\&quot;张三\&quot;,\&quot;email\&quot;:\&quot;zhangsan@example.com\&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">createUser</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                           HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">email</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;email&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=&quot;</span> + encoding);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        out.println(<span class="string">&quot;&#123;\&quot;success\&quot;:true,\&quot;message\&quot;:\&quot;用户创建成功\&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">updateUser</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                           HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">email</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;email&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=&quot;</span> + encoding);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        out.println(<span class="string">&quot;&#123;\&quot;success\&quot;:true,\&quot;message\&quot;:\&quot;用户更新成功\&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">deleteUser</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                            HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=&quot;</span> + encoding);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        out.println(<span class="string">&quot;&#123;\&quot;success\&quot;:true,\&quot;message\&quot;:\&quot;用户删除成功\&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">uploadAvatar</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                             HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取上传的文件</span></span><br><span class="line">        <span class="type">Part</span> <span class="variable">filePart</span> <span class="operator">=</span> request.getPart(<span class="string">&quot;avatar&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> filePart.getSubmittedFileName();</span><br><span class="line">        <span class="type">long</span> <span class="variable">fileSize</span> <span class="operator">=</span> filePart.getSize();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 保存文件</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">uploadPath</span> <span class="operator">=</span> getServletContext().getRealPath(<span class="string">&quot;/uploads/&quot;</span>);</span><br><span class="line">        java.io.<span class="type">File</span> <span class="variable">uploadDir</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">java</span>.io.File(uploadPath);</span><br><span class="line">        <span class="keyword">if</span> (!uploadDir.exists()) &#123;</span><br><span class="line">            uploadDir.mkdirs();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        filePart.write(uploadPath + java.io.File.separator + fileName);</span><br><span class="line">        </span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=&quot;</span> + encoding);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        out.println(<span class="string">&quot;&#123;\&quot;success\&quot;:true,\&quot;fileName\&quot;:\&quot;&quot;</span> + fileName + <span class="string">&quot;\&quot;,\&quot;size\&quot;:&quot;</span> + fileSize + <span class="string">&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、MVC-架构模式"><a href="#九、MVC-架构模式" class="headerlink" title="九、MVC 架构模式"></a>九、MVC 架构模式</h2><h3 id="9-1-MVC-模式概述"><a href="#9-1-MVC-模式概述" class="headerlink" title="9.1 MVC 模式概述"></a>9.1 MVC 模式概述</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🎯 MVC 架构模式"] --> B["👤 View\n（视图层）"]    A --> C["📋 Controller\n（控制器层）"]    A --> D["📊 Model\n（模型层）"]        B -->|"用户交互"| C    C -->|"调用"| D    D -->|"数据"| C    C -->|"返回视图"| B        B --> B1["JSP\nThymeleaf\nHTML+Ajax"]        C --> C1["处理请求\n参数校验\n调用 Service"]        D --> D1["业务逻辑\n数据访问\nEntity/DTO"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0</pre></div><h3 id="9-2-MVC-各层职责"><a href="#9-2-MVC-各层职责" class="headerlink" title="9.2 MVC 各层职责"></a>9.2 MVC 各层职责</h3><table><thead><tr><th>层级</th><th>职责</th><th>组成部分</th></tr></thead><tbody><tr><td><strong>Model</strong></td><td>业务逻辑和数据处理</td><td>Service、DAO、Entity</td></tr><tr><td><strong>View</strong></td><td>页面展示和用户交互</td><td>JSP、HTML、Thymeleaf</td></tr><tr><td><strong>Controller</strong></td><td>请求处理和流程控制</td><td>Servlet</td></tr></tbody></table><h3 id="9-3-简单-MVC-实现"><a href="#9-3-简单-MVC-实现" class="headerlink" title="9.3 简单 MVC 实现"></a>9.3 简单 MVC 实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Model: User 实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="comment">// getter/setter...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Model: UserDao 数据访问</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Map&lt;Long, User&gt; userMap = <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        userMap.put(<span class="number">1L</span>, <span class="keyword">new</span> <span class="title class_">User</span>(<span class="number">1L</span>, <span class="string">&quot;zhangsan&quot;</span>, <span class="string">&quot;zhangsan@example.com&quot;</span>, <span class="number">1</span>));</span><br><span class="line">        userMap.put(<span class="number">2L</span>, <span class="keyword">new</span> <span class="title class_">User</span>(<span class="number">2L</span>, <span class="string">&quot;lisi&quot;</span>, <span class="string">&quot;lisi@example.com&quot;</span>, <span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findAll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(userMap.values());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">findById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userMap.get(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">save</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (user.getId() == <span class="literal">null</span>) &#123;</span><br><span class="line">            user.setId((<span class="type">long</span>) (userMap.size() + <span class="number">1</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        userMap.put(user.getId(), user);</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userMap.remove(id) != <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Model: UserService 业务逻辑</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">UserDao</span> <span class="variable">userDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UserDao</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">getAllUsers</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userDao.findAll();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUserById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (id == <span class="literal">null</span> || id &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;ID 不合法&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> userDao.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">createUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="comment">// 业务校验</span></span><br><span class="line">        <span class="keyword">if</span> (user.getUsername() == <span class="literal">null</span> || user.getUsername().trim().isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;用户名不能为空&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> userDao.save(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Controller: UserController</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/mvc/users/*&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UserService</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="type">Gson</span> <span class="variable">gson</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Gson</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">pathInfo</span> <span class="operator">=</span> request.getPathInfo();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (pathInfo == <span class="literal">null</span> || pathInfo.equals(<span class="string">&quot;/&quot;</span>)) &#123;</span><br><span class="line">            <span class="comment">// 列出所有用户</span></span><br><span class="line">            listUsers(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (pathInfo.matches(<span class="string">&quot;/\\d+&quot;</span>)) &#123;</span><br><span class="line">            <span class="comment">// 获取指定用户</span></span><br><span class="line">            <span class="type">Long</span> <span class="variable">id</span> <span class="operator">=</span> Long.parseLong(pathInfo.substring(<span class="number">1</span>));</span><br><span class="line">            getUser(request, response, id);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            response.sendError(<span class="number">404</span>, <span class="string">&quot;Not Found&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doPost</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        request.setCharacterEncoding(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">String</span> <span class="variable">pathInfo</span> <span class="operator">=</span> request.getPathInfo();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;/&quot;</span>.equals(pathInfo)) &#123;</span><br><span class="line">            createUser(request, response);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            response.sendError(<span class="number">404</span>, <span class="string">&quot;Not Found&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">listUsers</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                          HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        List&lt;User&gt; users = userService.getAllUsers();</span><br><span class="line">        sendJson(response, users);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">getUser</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                        HttpServletResponse response, Long id)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userService.getUserById(id);</span><br><span class="line">            sendJson(response, user);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalArgumentException e) &#123;</span><br><span class="line">            response.sendError(<span class="number">404</span>, e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">createUser</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                           HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">BufferedReader</span> <span class="variable">reader</span> <span class="operator">=</span> request.getReader();</span><br><span class="line">        <span class="type">String</span> <span class="variable">body</span> <span class="operator">=</span> reader.lines().collect(Collectors.joining());</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> gson.fromJson(body, User.class);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">User</span> <span class="variable">created</span> <span class="operator">=</span> userService.createUser(user);</span><br><span class="line">            sendJson(response, created);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalArgumentException e) &#123;</span><br><span class="line">            response.sendError(<span class="number">400</span>, e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">sendJson</span><span class="params">(HttpServletResponse response, Object data)</span> </span><br><span class="line">            <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(gson.toJson(data));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十、常见问题与最佳实践"><a href="#十、常见问题与最佳实践" class="headerlink" title="十、常见问题与最佳实践"></a>十、常见问题与最佳实践</h2><h3 id="10-1-常见问题与解决方案"><a href="#10-1-常见问题与解决方案" class="headerlink" title="10.1 常见问题与解决方案"></a>10.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["⚠️ 404 找不到"]    A --> C["⚠️ 405 方法不支持"]    A --> D["⚠️ 中文乱码"]    A --> E["⚠️ Session 失效"]        B --> B1["检查 URL 映射\n检查 web.xml 配置"]        C --> C1["未重写 doGet/doPost\n或调用了父类 service()"]        D --> D1["设置请求/响应编码\nTomcat URIEncoding"]        E --> E1["Cookie 被禁用\n超时时间太短"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#fff3e0    style D fill:#e3f2fd    style E fill:#f8bbd0</pre></div><h3 id="10-2-最佳实践清单"><a href="#10-2-最佳实践清单" class="headerlink" title="10.2 最佳实践清单"></a>10.2 最佳实践清单</h3><table><thead><tr><th>实践</th><th>说明</th><th>推荐程度</th></tr></thead><tbody><tr><td><strong>使用注解配置</strong></td><td>减少 XML 配置，代码更简洁</td><td>✅✅✅</td></tr><tr><td><strong>统一字符编码</strong></td><td>过滤器中统一设置 UTF-8</td><td>✅✅✅</td></tr><tr><td><strong>使用 Session 存储敏感数据</strong></td><td>重要数据放服务器端</td><td>✅✅</td></tr><tr><td><strong>合理使用转发与重定向</strong></td><td>保持页面状态用转发，减少请求用重定向</td><td>✅✅</td></tr><tr><td><strong>线程安全</strong></td><td>Servlet 是单实例多线程，注意成员变量</td><td>✅✅✅</td></tr><tr><td><strong>资源释放</strong></td><td>关闭流、数据库连接等资源</td><td>✅✅✅</td></tr><tr><td><strong>日志记录</strong></td><td>使用日志框架记录关键操作</td><td>✅✅</td></tr></tbody></table><h3 id="10-3-线程安全注意事项"><a href="#10-3-线程安全注意事项" class="headerlink" title="10.3 线程安全注意事项"></a>10.3 线程安全注意事项</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ❌ 错误示例：使用成员变量（线程不安全）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/unsafe&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UnsafeServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;  <span class="comment">// ❌ 危险！所有请求共享这个变量</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> &#123;</span><br><span class="line">        count++;  <span class="comment">// ❌ 并发时计数错误</span></span><br><span class="line">        response.getWriter().write(<span class="string">&quot;访问次数：&quot;</span> + count);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ✅ 正确示例：使用局部变量（线程安全）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/safe&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SafeServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">localCount</span> <span class="operator">=</span> <span class="number">0</span>;  <span class="comment">// ✅ 每个请求独立的局部变量</span></span><br><span class="line">        localCount++;</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;访问次数：&quot;</span> + localCount);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ✅ 正确示例：使用请求域属性（线程安全）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/safe2&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SafeServlet2</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                        HttpServletResponse response)</span> &#123;</span><br><span class="line">        <span class="comment">// ✅ 使用请求域存储数据</span></span><br><span class="line">        request.setAttribute(<span class="string">&quot;count&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">count</span> <span class="operator">=</span> (Integer) request.getAttribute(<span class="string">&quot;count&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;访问次数：&quot;</span> + count);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ✅ 正确示例：使用同步块（需要共享数据时）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/synced&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SyncedServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;  <span class="comment">// ✅ 加同步块</span></span><br><span class="line">            count++;</span><br><span class="line">            response.getWriter().write(<span class="string">&quot;访问次数：&quot;</span> + count);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十一、总结"><a href="#十一、总结" class="headerlink" title="十一、总结"></a>十一、总结</h2><h3 id="11-1-核心知识点回顾"><a href="#11-1-核心知识点回顾" class="headerlink" title="11.1 核心知识点回顾"></a>11.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Servlet 核心技术))    基础概念      Servlet 定义      Servlet 容器      Tomcat 服务器    生命周期      实例化      初始化 init      服务 service      销毁 destroy    请求响应      HttpServletRequest      HttpServletResponse      参数获取      中文乱码    会话管理      Cookie      Session      区别对比    过滤器监听器      Filter 过滤器      FilterChain      Listener 监听器    注解开发      @WebServlet      @WebFilter      @WebListener    MVC 架构      Model 层      View 层      Controller 层</pre></div><h3 id="11-2-学习路线建议"><a href="#11-2-学习路线建议" class="headerlink" title="11.2 学习路线建议"></a>11.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["学习建议"] --> B["📖 理解原理"]    A --> C["💻 多敲代码"]    A --> D["🔍 调试跟踪"]    A --> E["📚 阅读源码"]        B --> B1["生命周期\n请求分发\n工作原理"]        C --> C1["手写 Servlet\n实现 CRUD\n过滤器拦截器"]        D --> D1["断点调试\n查看 Tomcat 日志"]        E --> E1["Spring MVC\nTomcat 源码"]        style A fill:#fff3e0</pre></div><h3 id="11-3-下一步推荐学习"><a href="#11-3-下一步推荐学习" class="headerlink" title="11.3 下一步推荐学习"></a>11.3 下一步推荐学习</h3><ul><li>📖 <strong>Spring MVC</strong>：Servlet 的高级封装，Java Web 开发主流框架</li><li>📖 <strong>Spring Boot</strong>：简化 Spring 开发，快速构建 Web 应用</li><li>📖 <strong>Tomcat 源码</strong>：深入理解 Servlet 容器的工作原理</li><li>📖 **Servlet 4.0+**：HTTP&#x2F;2、Server Push 等新特性</li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Servlet 是 Java Web 开发的基石，虽然现在很少有人直接手写 Servlet（大多数使用 Spring MVC 等框架），但理解 Servlet 的原理对于深入学习 Spring 框架和排查问题非常有帮助。”知其然，更知其所以然”，共勉！🚀</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/servlet-complete-guide/</id>
    <link href="https://blog.codenav.top/servlet-complete-guide/"/>
    <published>2026-05-24T05:46:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Servlet-技术详解：从入门到实战-🦞"><a href="#Servlet-技术详解：从入门到实战-🦞" class="headerlink" title="Servlet 技术详解：从入门到实战 🦞"></a>Servlet 技术详解：从入门到实战]]>
    </summary>
    <title>Servlet 技术详解：从入门到实战 🦞</title>
    <updated>2026-05-24T05:48:06.627Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="学习路线" scheme="https://blog.codenav.top/tags/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/"/>
    <category term="职业规划" scheme="https://blog.codenav.top/tags/%E8%81%8C%E4%B8%9A%E8%A7%84%E5%88%92/"/>
    <content>
      <![CDATA[<h1 id="Java-学习路线完整指南：从入门到就业-🌟"><a href="#Java-学习路线完整指南：从入门到就业-🌟" class="headerlink" title="Java 学习路线完整指南：从入门到就业 🌟"></a>Java 学习路线完整指南：从入门到就业 🌟</h1><blockquote><p>Java 作为全球最流行的编程语言之一，在企业级开发领域占据着举足轻重的地位。本文将为你规划一条清晰、系统、高效的 Java 学习路线，无论你是零基础的编程小白，还是希望转型 Java 开发的工程师，都能从中找到适合自己的学习方向。🎯</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-java">一、为什么选择 Java？</a></li><li><a href="#%E4%BA%8C%E6%95%B4%E4%BD%93%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF%E5%9B%BE">二、整体学习路线图</a></li><li><a href="#%E4%B8%89%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8">三、第一阶段：编程基础入门</a></li><li><a href="#%E5%9B%9B%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5java-%E6%A0%B8%E5%BF%83%E6%8A%80%E8%83%BD">四、第二阶段：Java 核心技能</a></li><li><a href="#%E4%BA%94%E7%AC%AC%E4%B8%89%E9%98%B6%E6%AE%B5%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E%E6%8C%81%E4%B9%85%E5%B1%82">五、第三阶段：数据库与持久层</a></li><li><a href="#%E5%85%AD%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5web-%E5%BC%80%E5%8F%91%E5%9F%BA%E7%A1%80">六、第四阶段：Web 开发基础</a></li><li><a href="#%E4%B8%83%E7%AC%AC%E4%BA%94%E9%98%B6%E6%AE%B5%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E8%BF%9B%E9%98%B6">七、第五阶段：主流框架进阶</a></li><li><a href="#%E5%85%AB%E7%AC%AC%E5%85%AD%E9%98%B6%E6%AE%B5%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F">八、第六阶段：微服务与分布式</a></li><li><a href="#%E4%B9%9D%E7%AC%AC%E4%B8%83%E9%98%B6%E6%AE%B5devops-%E4%B8%8E%E5%B7%A5%E5%85%B7%E9%93%BE">九、第七阶段：DevOps 与工具链</a></li><li><a href="#%E5%8D%81%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95%E4%B8%8E%E8%B5%84%E6%BA%90%E6%8E%A8%E8%8D%90">十、学习方法与资源推荐</a></li><li><a href="#%E5%8D%81%E4%B8%80%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98-faq">十一、常见问题 FAQ</a></li></ul><hr><h2 id="一、为什么选择-Java？"><a href="#一、为什么选择-Java？" class="headerlink" title="一、为什么选择 Java？"></a>一、为什么选择 Java？</h2><h3 id="1-1-Java-的优势"><a href="#1-1-Java-的优势" class="headerlink" title="1.1 Java 的优势"></a>1.1 Java 的优势</h3><p>Java 自 1995 年诞生至今，已经成为全球最受欢迎的编程语言之一。选择 Java，你将获得：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["☕️ Java 语言优势"] --> B["🌐 应用广泛"]    A --> C["💼 就业前景好"]    A --> D["🔒 安全稳定"]    A --> E["📈 生态完善"]    A --> F["🚀 性能优良"]        B --> B1["企业级应用\nAndroid 开发\n大数据处理\n云计算平台"]        C --> C1["人才需求大\n薪资水平高\n职业路径清晰"]        D --> D1["强类型语言\n垃圾回收机制\n安全沙箱执行"]        E --> E1["Spring 生态\n丰富的开源库\n完善的工具链"]        F --> F1["JIT 编译优化\n高性能 JVM\n高并发支持"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#f8bbd0    style F fill:#fff3e0</pre></div><h3 id="1-2-Java-的应用领域"><a href="#1-2-Java-的应用领域" class="headerlink" title="1.2 Java 的应用领域"></a>1.2 Java 的应用领域</h3><table><thead><tr><th>应用领域</th><th>典型技术栈</th><th>代表岗位</th></tr></thead><tbody><tr><td><strong>企业级应用</strong></td><td>Spring Boot + MyBatis + MySQL</td><td>后端开发工程师</td></tr><tr><td><strong>微服务架构</strong></td><td>Spring Cloud + Docker + K8s</td><td>高级后端工程师</td></tr><tr><td><strong>大数据开发</strong></td><td>Hadoop + Spark + Flink</td><td>大数据工程师</td></tr><tr><td><strong>Android 开发</strong></td><td>Kotlin + Jetpack</td><td>Android 开发工程师</td></tr><tr><td><strong>游戏开发</strong></td><td>LibGDX + Java</td><td>游戏开发工程师</td></tr></tbody></table><hr><h2 id="二、整体学习路线图"><a href="#二、整体学习路线图" class="headerlink" title="二、整体学习路线图"></a>二、整体学习路线图</h2><h3 id="2-1-六阶段学习路线"><a href="#2-1-六阶段学习路线" class="headerlink" title="2.1 六阶段学习路线"></a>2.1 六阶段学习路线</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["第一阶段\n编程基础"] --> B["第二阶段\nJava 核心"]    B --> C["第三阶段\n数据库持久层"]    C --> D["第四阶段\nWeb 开发"]    D --> E["第五阶段\n主流框架"]    E --> F["第六阶段\n微服务架构"]        A --> A1["Java 环境搭建"]    A --> A2["基本语法"]    A --> A3["面向对象"]    A --> A4["异常处理"]    A --> A5["常用类库"]        B --> B1["集合框架"]    B --> B2["多线程"]    B --> B3["I/O 网络"]    B --> B4["JVM 原理"]        C --> C1["MySQL 数据库"]    C --> C2["JDBC"]    C --> C3["MyBatis"]    C --> C4["Hibernate"]        D --> D1["HTML/CSS/JS"]    D --> D2["Servlet/JSP"]    D --> D3["Spring MVC"]    D --> D4["Spring Boot"]        E --> E1["Spring Boot 深入"]    E --> E2["Spring Cloud"]    E --> E3["Redis 缓存"]    E --> E4["MQ 消息队列"]        F --> F1["微服务架构"]    F --> F2["Docker 容器化"]    F --> F3["K8s 编排"]    F --> F4["CI/CD 持续集成"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0    style E fill:#ffcdd2    style F fill:#e3f2fd</pre></div><h3 id="2-2-学习时间规划建议"><a href="#2-2-学习时间规划建议" class="headerlink" title="2.2 学习时间规划建议"></a>2.2 学习时间规划建议</h3><table><thead><tr><th>阶段</th><th>学习内容</th><th>推荐时长</th><th>难度</th></tr></thead><tbody><tr><td><strong>第一阶段</strong></td><td>Java 基础语法 + OOP</td><td>4-6 周</td><td>⭐</td></tr><tr><td><strong>第二阶段</strong></td><td>集合&#x2F;多线程&#x2F;I&#x2F;O</td><td>4-6 周</td><td>⭐⭐⭐</td></tr><tr><td><strong>第三阶段</strong></td><td>数据库 + 持久层框架</td><td>3-4 周</td><td>⭐⭐</td></tr><tr><td><strong>第四阶段</strong></td><td>Web 开发基础</td><td>3-4 周</td><td>⭐⭐</td></tr><tr><td><strong>第五阶段</strong></td><td>Spring 全家桶</td><td>4-6 周</td><td>⭐⭐⭐</td></tr><tr><td><strong>第六阶段</strong></td><td>微服务 + 工具链</td><td>4-8 周</td><td>⭐⭐⭐⭐</td></tr></tbody></table><blockquote><p>💡 <strong>说明</strong>：以上时间为每天投入 4-6 小时学习的建议时长，可根据个人基础适当调整。</p></blockquote><hr><h2 id="三、第一阶段：编程基础入门"><a href="#三、第一阶段：编程基础入门" class="headerlink" title="三、第一阶段：编程基础入门"></a>三、第一阶段：编程基础入门</h2><h3 id="3-1-Java-环境搭建"><a href="#3-1-Java-环境搭建" class="headerlink" title="3.1 Java 环境搭建"></a>3.1 Java 环境搭建</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 下载 JDK（推荐 JDK 17 或 JDK 21）</span></span><br><span class="line"><span class="comment"># 下载地址：https://adoptium.net/</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 配置环境变量</span></span><br><span class="line"><span class="comment"># Windows：在系统环境变量中添加</span></span><br><span class="line"><span class="comment"># JAVA_HOME = D:\Java\jdk-17</span></span><br><span class="line"><span class="comment"># PATH = %JAVA_HOME%\bin</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 验证安装</span></span><br><span class="line">java -version</span><br><span class="line">javac -version</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 安装 IDE（IntelliJ IDEA 社区版免费）</span></span><br><span class="line"><span class="comment"># 下载地址：https://www.jetbrains.com/idea/download/</span></span><br></pre></td></tr></table></figure><h3 id="3-2-第一个-Java-程序"><a href="#3-2-第一个-Java-程序" class="headerlink" title="3.2 第一个 Java 程序"></a>3.2 第一个 Java 程序</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Hello World - 你的第一个 Java 程序</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorld</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Hello, World!&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;欢迎来到 Java 编程世界！🚀&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>编译运行步骤：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编译</span></span><br><span class="line">javac HelloWorld.java</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行</span></span><br><span class="line">java HelloWorld</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出：</span></span><br><span class="line"><span class="comment"># Hello, World!</span></span><br><span class="line"><span class="comment"># 欢迎来到 Java 编程世界！🚀</span></span><br></pre></td></tr></table></figure><h3 id="3-3-基础语法要点"><a href="#3-3-基础语法要点" class="headerlink" title="3.3 基础语法要点"></a>3.3 基础语法要点</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Java 基础语法速查表</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BasicSyntax</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 变量与数据类型 ==========</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 整数类型</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">age</span> <span class="operator">=</span> <span class="number">25</span>;                    <span class="comment">// 4 字节，范围：-21亿 ~ 21亿</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">bigNumber</span> <span class="operator">=</span> <span class="number">1234567890L</span>;     <span class="comment">// 8 字节</span></span><br><span class="line">        <span class="type">short</span> <span class="variable">smallNumber</span> <span class="operator">=</span> <span class="number">100</span>;          <span class="comment">// 2 字节</span></span><br><span class="line">        <span class="type">byte</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">127</span>;                     <span class="comment">// 1 字节</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 浮点类型</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">price</span> <span class="operator">=</span> <span class="number">99.99</span>;            <span class="comment">// 8 字节双精度</span></span><br><span class="line">        <span class="type">float</span> <span class="variable">rate</span> <span class="operator">=</span> <span class="number">3.14f</span>;               <span class="comment">// 4 字节单精度，需要加 f</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 字符与字符串</span></span><br><span class="line">        <span class="type">char</span> <span class="variable">grade</span> <span class="operator">=</span> <span class="string">&#x27;A&#x27;</span>;                 <span class="comment">// 单个字符</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;Java&quot;</span>;             <span class="comment">// 字符串（对象类型）</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 布尔类型</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isStudent</span> <span class="operator">=</span> <span class="literal">true</span>;        <span class="comment">// true / false</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 运算符 ==========</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 算术运算符：+ - * / %</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">10</span> + <span class="number">5</span>;                <span class="comment">// 15</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">diff</span> <span class="operator">=</span> <span class="number">10</span> - <span class="number">5</span>;               <span class="comment">// 5</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">product</span> <span class="operator">=</span> <span class="number">10</span> * <span class="number">5</span>;             <span class="comment">// 50</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">quotient</span> <span class="operator">=</span> <span class="number">10</span> / <span class="number">3</span>;            <span class="comment">// 3（整数除法）</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">remainder</span> <span class="operator">=</span> <span class="number">10</span> % <span class="number">3</span>;            <span class="comment">// 1</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 比较运算符：== != &gt; &lt; &gt;= &lt;=</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isEqual</span> <span class="operator">=</span> (<span class="number">5</span> == <span class="number">5</span>);       <span class="comment">// true</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isGreater</span> <span class="operator">=</span> (<span class="number">10</span> &gt; <span class="number">5</span>);     <span class="comment">// true</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 逻辑运算符：&amp;&amp; || !</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">logic</span> <span class="operator">=</span> (<span class="number">5</span> &gt; <span class="number">3</span>) &amp;&amp; (<span class="number">10</span> &lt; <span class="number">20</span>);  <span class="comment">// true</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 控制流程 ==========</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// if-else 条件判断</span></span><br><span class="line">        <span class="keyword">if</span> (age &gt;= <span class="number">18</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;成年人&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;未成年人&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// switch 多分支</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">day</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line">        <span class="keyword">switch</span> (day) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">1</span>: System.out.println(<span class="string">&quot;星期一&quot;</span>); <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">2</span>: System.out.println(<span class="string">&quot;星期二&quot;</span>); <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">3</span>: System.out.println(<span class="string">&quot;星期三&quot;</span>); <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">default</span>: System.out.println(<span class="string">&quot;其他&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// for 循环</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">5</span>; i++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;第 &quot;</span> + i + <span class="string">&quot; 次循环&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// while 循环</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (count &lt; <span class="number">3</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;while 循环：&quot;</span> + count);</span><br><span class="line">            count++;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// foreach 增强循环</span></span><br><span class="line">        String[] fruits = &#123;<span class="string">&quot;苹果&quot;</span>, <span class="string">&quot;香蕉&quot;</span>, <span class="string">&quot;橙子&quot;</span>&#125;;</span><br><span class="line">        <span class="keyword">for</span> (String fruit : fruits) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;水果：&quot;</span> + fruit);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-面向对象编程（OOP）"><a href="#3-4-面向对象编程（OOP）" class="headerlink" title="3.4 面向对象编程（OOP）"></a>3.4 面向对象编程（OOP）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 类与对象：面向对象的核心概念</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个学生类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ========== 属性（字段）==========</span></span><br><span class="line">    <span class="keyword">private</span> String name;        <span class="comment">// 姓名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;            <span class="comment">// 年龄</span></span><br><span class="line">    <span class="keyword">private</span> String studentId;   <span class="comment">// 学号</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ========== 构造方法 ==========</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 无参构造</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 有参构造</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">(String name, <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 全参构造</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">(String name, <span class="type">int</span> age, String studentId)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        <span class="built_in">this</span>.studentId = studentId;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ========== 方法 ==========</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">study</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在学习...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">study</span><span class="params">(String subject)</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在学习 &quot;</span> + subject);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getInfo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;学生信息：姓名=&quot;</span> + name + <span class="string">&quot;, 年龄=&quot;</span> + age + <span class="string">&quot;, 学号=&quot;</span> + studentId;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ========== Getter 和 Setter ==========</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (age &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.age = age;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getStudentId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> studentId;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setStudentId</span><span class="params">(String studentId)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.studentId = studentId;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用示例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OOPDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建对象</span></span><br><span class="line">        <span class="type">Student</span> <span class="variable">student1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">&quot;张三&quot;</span>, <span class="number">20</span>, <span class="string">&quot;S001&quot;</span>);</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">&quot;李四&quot;</span>, <span class="number">21</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 调用方法</span></span><br><span class="line">        student1.study();</span><br><span class="line">        student1.study(<span class="string">&quot;Java 编程&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        System.out.println(student1.getInfo());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 使用 getter/setter</span></span><br><span class="line">        student2.setAge(<span class="number">22</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;年龄：&quot;</span> + student2.getAge());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-5-三大特性详解"><a href="#3-5-三大特性详解" class="headerlink" title="3.5 三大特性详解"></a>3.5 三大特性详解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🎯 面向对象三大特性"] --> B["🔒 封装"]    A --> C["🔄 继承"]    A --> D["🎭 多态"]        B --> B1["隐藏实现细节\n只暴露必要接口\n保护数据安全"]        C --> C1["代码复用\n子类继承父类\n扩展新功能"]        D --> D1["同一种表现形态\n不同实现方式\n提高扩展性"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#f8bbd0</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 封装示例：使用访问修饰符保护数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EncapsulationDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String name;      <span class="comment">// private：类内可见</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;         <span class="comment">// 外部不能直接访问</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 通过 public 方法提供受控访问</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (age &lt; <span class="number">0</span> || age &gt; <span class="number">150</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;年龄不合法&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 继承示例：extends 关键字</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">// 父类：动物</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line">    <span class="keyword">protected</span> <span class="type">int</span> age;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在吃东西&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sleep</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在睡觉&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子类：狗（继承自动物）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String breed;  <span class="comment">// 品种</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 子类特有方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">bark</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 汪汪叫！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 重写父类方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在吃狗粮&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用示例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InheritanceDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Dog</span> <span class="variable">dog</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>();</span><br><span class="line">        dog.name = <span class="string">&quot;旺财&quot;</span>;</span><br><span class="line">        dog.eat();    <span class="comment">// 调用重写后的方法</span></span><br><span class="line">        dog.bark();   <span class="comment">// 子类特有方法</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多态示例：父类引用指向子类对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PolymorphismDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 父类引用指向子类对象</span></span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>();  <span class="comment">// 向上转型</span></span><br><span class="line">        animal1.eat();               <span class="comment">// 调用 Dog 的 eat()</span></span><br><span class="line">        </span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>();</span><br><span class="line">        animal2.eat();               <span class="comment">// 调用 Cat 的 eat()</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 数组的多态</span></span><br><span class="line">        Animal[] animals = &#123;<span class="keyword">new</span> <span class="title class_">Dog</span>(), <span class="keyword">new</span> <span class="title class_">Cat</span>(), <span class="keyword">new</span> <span class="title class_">Bird</span>()&#125;;</span><br><span class="line">        <span class="keyword">for</span> (Animal animal : animals) &#123;</span><br><span class="line">            animal.eat();  <span class="comment">// 调用各自重写的 eat()</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、第二阶段：Java-核心技能"><a href="#四、第二阶段：Java-核心技能" class="headerlink" title="四、第二阶段：Java 核心技能"></a>四、第二阶段：Java 核心技能</h2><h3 id="4-1-集合框架（Collection）"><a href="#4-1-集合框架（Collection）" class="headerlink" title="4.1 集合框架（Collection）"></a>4.1 集合框架（Collection）</h3><p>集合是 Java 开发中最常用的数据结构，必须熟练掌握：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📦 Java 集合框架"] --> B["Collection 接口"]    A --> C["Map 接口"]        B --> B1["List 接口\n（有序、可重复）"]    B --> B2["Set 接口\n（无序、不可重复）"]    B --> B3["Queue 接口\n（队列）"]        C --> C1["HashMap\n（哈希表）"]    C --> C2["TreeMap\n（红黑树）"]    C --> C3["LinkedHashMap\n（链表+哈希）"]        B1 --> B1a["ArrayList"]    B1 --> B1b["LinkedList"]    B1 --> B1c["Vector"]        B2 --> B2a["HashSet"]    B2 --> B2b["TreeSet"]    B2 --> B2c["LinkedHashSet"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 集合框架使用示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CollectionDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== List：有序、可重复 ==========</span></span><br><span class="line">        </span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        list.add(<span class="string">&quot;Java&quot;</span>);</span><br><span class="line">        list.add(<span class="string">&quot;Python&quot;</span>);</span><br><span class="line">        list.add(<span class="string">&quot;Java&quot;</span>);  <span class="comment">// 可以重复添加</span></span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;List 大小：&quot;</span> + list.size());  <span class="comment">// 3</span></span><br><span class="line">        System.out.println(<span class="string">&quot;第一个元素：&quot;</span> + list.get(<span class="number">0</span>));  <span class="comment">// Java</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Java 首次出现位置：&quot;</span> + list.indexOf(<span class="string">&quot;Java&quot;</span>));  <span class="comment">// 0</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 遍历</span></span><br><span class="line">        <span class="keyword">for</span> (String item : list) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;元素：&quot;</span> + item);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== Set：无序、不可重复 ==========</span></span><br><span class="line">        </span><br><span class="line">        Set&lt;String&gt; set = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">        set.add(<span class="string">&quot;Apple&quot;</span>);</span><br><span class="line">        set.add(<span class="string">&quot;Banana&quot;</span>);</span><br><span class="line">        set.add(<span class="string">&quot;Apple&quot;</span>);  <span class="comment">// 重复添加被忽略</span></span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;Set 大小：&quot;</span> + set.size());  <span class="comment">// 2</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== Map：键值对 ==========</span></span><br><span class="line">        </span><br><span class="line">        Map&lt;String, Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;Java&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        map.put(<span class="string">&quot;Python&quot;</span>, <span class="number">2</span>);</span><br><span class="line">        map.put(<span class="string">&quot;C++&quot;</span>, <span class="number">3</span>);</span><br><span class="line">        map.put(<span class="string">&quot;Java&quot;</span>, <span class="number">4</span>);  <span class="comment">// key 相同，覆盖前面的值</span></span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;Map 大小：&quot;</span> + map.size());  <span class="comment">// 3</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Java 对应的值：&quot;</span> + map.get(<span class="string">&quot;Java&quot;</span>));  <span class="comment">// 4</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 遍历 Map</span></span><br><span class="line">        <span class="keyword">for</span> (Map.Entry&lt;String, Integer&gt; entry : map.entrySet()) &#123;</span><br><span class="line">            System.out.println(entry.getKey() + <span class="string">&quot; -&gt; &quot;</span> + entry.getValue());</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 常见面试题：ArrayList vs LinkedList ==========</span></span><br><span class="line">        <span class="comment">// ArrayList：基于数组，随机访问快（O(1)），插入删除慢（O(n)）</span></span><br><span class="line">        <span class="comment">// LinkedList：基于链表，插入删除快（O(1)），随机访问慢（O(n)）</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// ========== 常见面试题：HashMap vs HashSet ==========</span></span><br><span class="line">        <span class="comment">// HashMap：存储键值对，key 无重复</span></span><br><span class="line">        <span class="comment">// HashSet：存储单独元素，元素无重复（底层基于 HashMap）</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-2-多线程编程"><a href="#4-2-多线程编程" class="headerlink" title="4.2 多线程编程"></a>4.2 多线程编程</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多线程创建方式一：继承 Thread 类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;线程 &quot;</span> + getName() + <span class="string">&quot; 正在执行...&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">5</span>; i++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;线程 &quot;</span> + getName() + <span class="string">&quot; - 第 &quot;</span> + i + <span class="string">&quot; 次执行&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadDemo1</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">MyThread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br><span class="line">        thread1.start();  <span class="comment">// 启动线程</span></span><br><span class="line">        </span><br><span class="line">        <span class="type">MyThread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br><span class="line">        thread2.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多线程创建方式二：实现 Runnable 接口（更常用）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String taskName;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyRunnable</span><span class="params">(String taskName)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.taskName = taskName;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;任务 &quot;</span> + taskName + <span class="string">&quot; 开始执行&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span>);  <span class="comment">// 模拟任务执行</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;任务 &quot;</span> + taskName + <span class="string">&quot; 执行完成&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadDemo2</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">MyRunnable</span>(<span class="string">&quot;下载文件&quot;</span>));</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">MyRunnable</span>(<span class="string">&quot;处理数据&quot;</span>));</span><br><span class="line">        </span><br><span class="line">        thread1.start();</span><br><span class="line">        thread2.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多线程创建方式三：Lambda 表达式（最简洁）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadDemo3</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 使用 Lambda 创建 Runnable</span></span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">task1</span> <span class="operator">=</span> () -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;任务 A 执行中...&quot;</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">task2</span> <span class="operator">=</span> () -&gt; System.out.println(<span class="string">&quot;任务 B 执行中...&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(task1).start();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(task2).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 线程同步：synchronized 关键字</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SynchronizedDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// synchronized 保证线程安全</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">increment</span><span class="params">()</span> &#123;</span><br><span class="line">        count++;</span><br><span class="line">        System.out.println(<span class="string">&quot;计数器：&quot;</span> + count);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SynchronizedDemo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SynchronizedDemo</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建 10 个线程同时执行 increment</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; demo.increment()).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-3-I-O-流与文件处理"><a href="#4-3-I-O-流与文件处理" class="headerlink" title="4.3 I&#x2F;O 流与文件处理"></a>4.3 I&#x2F;O 流与文件处理</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 文件读写示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IOdemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 字节流读写文件</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">byteStreamDemo</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 写文件</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> <span class="string">&quot;Hello, Java I/O! 🧡&quot;</span>;</span><br><span class="line">        <span class="type">FileOutputStream</span> <span class="variable">fos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line">        fos.write(content.getBytes());</span><br><span class="line">        fos.close();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 读文件</span></span><br><span class="line">        <span class="type">FileInputStream</span> <span class="variable">fis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line">        <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> fis.read(buffer);</span><br><span class="line">        <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(buffer, <span class="number">0</span>, len);</span><br><span class="line">        System.out.println(<span class="string">&quot;读取内容：&quot;</span> + result);</span><br><span class="line">        fis.close();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 字符流读写文件</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">charStreamDemo</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 写文件</span></span><br><span class="line">        <span class="type">FileWriter</span> <span class="variable">fw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileWriter</span>(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line">        fw.write(<span class="string">&quot;使用字符流写入内容&quot;</span>);</span><br><span class="line">        fw.close();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 读文件</span></span><br><span class="line">        <span class="type">FileReader</span> <span class="variable">fr</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileReader</span>(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line">        <span class="type">BufferedReader</span> <span class="variable">br</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(fr);</span><br><span class="line">        String line;</span><br><span class="line">        <span class="keyword">while</span> ((line = br.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">            System.out.println(line);</span><br><span class="line">        &#125;</span><br><span class="line">        br.close();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 使用 try-with-resources 自动关闭流（推荐）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">tryWithResourcesDemo</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 自动关闭资源，无需 finally</span></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">FileWriter</span> <span class="variable">fw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileWriter</span>(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line">             <span class="type">BufferedWriter</span> <span class="variable">bw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedWriter</span>(fw)) &#123;</span><br><span class="line">            bw.write(<span class="string">&quot;第一行内容&quot;</span>);</span><br><span class="line">            bw.newLine();</span><br><span class="line">            bw.write(<span class="string">&quot;第二行内容&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> (<span class="type">FileReader</span> <span class="variable">fr</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileReader</span>(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line">             <span class="type">BufferedReader</span> <span class="variable">br</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(fr)) &#123;</span><br><span class="line">            br.lines().forEach(System.out::println);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-4-JVM-核心原理"><a href="#4-4-JVM-核心原理" class="headerlink" title="4.4 JVM 核心原理"></a>4.4 JVM 核心原理</h3><p>理解 JVM 是成为高级 Java 开发者的必经之路：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔧 JVM 架构"] --> B["类加载器\nClassLoader"]    A --> C["运行时数据区\nRuntime Data Area"]    A --> D["执行引擎\nExecution Engine"]        C --> C1["堆 Heap"]    C --> C2["栈 Stack"]    C --> C3["方法区\nMethod Area"]    C --> C4["程序计数器\nPC Register"]        B --> B1["Bootstrap CL"]    B --> B2["Extension CL"]    B --> B3["Application CL"]        style A fill:#fff3e0    style C1 fill:#c8e6c9    style C2 fill:#e3f2fd    style C3 fill:#fff3e0    style C4 fill:#f8bbd0</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * JVM 内存区域示例：帮助理解内存分配</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JVMMemoryDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 方法区：存储类信息、常量、静态变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">staticVar</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONSTANT</span> <span class="operator">=</span> <span class="string">&quot;常量&quot;</span>;  <span class="comment">// 常量存储在常量池</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 栈帧：每个方法调用都会创建一个栈帧</span></span><br><span class="line">        methodA();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">methodA</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 局部变量表：存储方法参数和局部变量</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">localVar</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;局部变量&quot;</span>;  <span class="comment">// 引用类型，引用存在栈，实际对象在堆</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 堆：所有对象都在堆上分配</span></span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">&quot;张三&quot;</span>, <span class="number">25</span>);</span><br><span class="line">        methodB(person);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">methodB</span><span class="params">(Person p)</span> &#123;</span><br><span class="line">        <span class="comment">// 操作数栈：方法执行时的临时数据存储</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> p.getAge() + <span class="number">10</span>;</span><br><span class="line">        System.out.println(result);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对象在堆中的内存分配</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;  <span class="comment">// 引用类型，4字节（reference）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;      <span class="comment">// 基本类型，4字节</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name, <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、第三阶段：数据库与持久层"><a href="#五、第三阶段：数据库与持久层" class="headerlink" title="五、第三阶段：数据库与持久层"></a>五、第三阶段：数据库与持久层</h2><h3 id="5-1-MySQL-数据库"><a href="#5-1-MySQL-数据库" class="headerlink" title="5.1 MySQL 数据库"></a>5.1 MySQL 数据库</h3><p>详见博客文章：<a href="/tags/MySQL/">MySQL 数据库从入门到精通</a></p><h3 id="5-2-JDBC-数据库连接"><a href="#5-2-JDBC-数据库连接" class="headerlink" title="5.2 JDBC 数据库连接"></a>5.2 JDBC 数据库连接</h3><p>详见博客文章：<a href="/tags/JDBC/">JDBC 零基础入门到实战</a></p><h3 id="5-3-MyBatis-持久层框架"><a href="#5-3-MyBatis-持久层框架" class="headerlink" title="5.3 MyBatis 持久层框架"></a>5.3 MyBatis 持久层框架</h3><p>详见博客文章：<a href="/tags/MyBatis/">MyBatis 从入门到精通</a></p><h3 id="5-4-学习建议"><a href="#5-4-学习建议" class="headerlink" title="5.4 学习建议"></a>5.4 学习建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["💾 数据库持久层学习建议"] --> B["📖 先学理论"]    A --> C["💻 多敲代码"]    A --> D["🔍 理解原理"]    A --> E["🚀 勤加练习"]        B --> B1["SQL 基础\nJDBC 原理\n连接池概念"]    C --> C1["CRUD 练习\n事务控制\n性能优化"]    D --> D1["预编译原理\n缓存机制\nORM 映射"]    E --> E1["博客系统\n电商系统\n管理系统"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#f8bbd0</pre></div><hr><h2 id="六、第四阶段：Web-开发基础"><a href="#六、第四阶段：Web-开发基础" class="headerlink" title="六、第四阶段：Web 开发基础"></a>六、第四阶段：Web 开发基础</h2><h3 id="6-1-HTML-CSS-JavaScript-基础"><a href="#6-1-HTML-CSS-JavaScript-基础" class="headerlink" title="6.1 HTML&#x2F;CSS&#x2F;JavaScript 基础"></a>6.1 HTML&#x2F;CSS&#x2F;JavaScript 基础</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 简单的 HTML 页面 --&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;zh-CN&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>我的第一个网页<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">body</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">font-family</span>: Arial, sans-serif;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">margin</span>: <span class="number">20px</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">h1</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">color</span>: <span class="number">#333</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.container</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">max-width</span>: <span class="number">800px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">margin</span>: <span class="number">0</span> auto;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;container&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h1</span>&gt;</span>🎉 欢迎学习 Web 开发<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>这是一个简单的 HTML 页面示例<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">&quot;sayHello()&quot;</span>&gt;</span>点击我<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">function</span> <span class="title function_">sayHello</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">alert</span>(<span class="string">&quot;Hello, JavaScript! 🚀&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="6-2-HTTP-协议基础"><a href="#6-2-HTTP-协议基础" class="headerlink" title="6.2 HTTP 协议基础"></a>6.2 HTTP 协议基础</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HTTP 请求方法示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HTTPMethods</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// GET 请求：获取资源</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">httpGetDemo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// URL 参数拼接在地址栏</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;https://api.example.com/users?id=1&quot;</span>;</span><br><span class="line">        <span class="comment">// 特点：无请求体，参数有长度限制</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// POST 请求：提交数据</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">httpPostDemo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 请求参数放在请求体中</span></span><br><span class="line">        <span class="comment">// 适合发送大量数据</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// PUT 请求：更新资源</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">httpPutDemo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 幂等操作，多次执行结果相同</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// DELETE 请求：删除资源</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">httpDeleteDemo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 用于删除服务器上的资源</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-Servlet-与-JSP"><a href="#6-3-Servlet-与-JSP" class="headerlink" title="6.3 Servlet 与 JSP"></a>6.3 Servlet 与 JSP</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Servlet 示例：处理 HTTP 请求</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UserServiceImpl</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, </span></span><br><span class="line"><span class="params">                        HttpServletResponse response)</span> </span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取请求参数</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 业务处理</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userService.findById(Long.parseLong(id));</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置响应内容</span></span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json;charset=UTF-8&quot;</span>);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;&quot;</span> + user.getName() + <span class="string">&quot;\&quot;&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doPost</span><span class="params">(HttpServletRequest request,</span></span><br><span class="line"><span class="params">                         HttpServletResponse response)</span></span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 处理 POST 请求（添加用户）</span></span><br><span class="line">        request.setCharacterEncoding(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">email</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;email&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">        user.setName(name);</span><br><span class="line">        user.setEmail(email);</span><br><span class="line">        </span><br><span class="line">        userService.save(user);</span><br><span class="line">        response.getWriter().write(<span class="string">&quot;&#123;\&quot;success\&quot;:true&#125;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、第五阶段：主流框架进阶"><a href="#七、第五阶段：主流框架进阶" class="headerlink" title="七、第五阶段：主流框架进阶"></a>七、第五阶段：主流框架进阶</h2><h3 id="7-1-Spring-Boot-快速开发"><a href="#7-1-Spring-Boot-快速开发" class="headerlink" title="7.1 Spring Boot 快速开发"></a>7.1 Spring Boot 快速开发</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring Boot 启动类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Application</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(Application.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RESTful API 控制器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/api/users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService userService;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * GET /api/users - 获取所有用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">getAllUsers</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userService.findAll();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * GET /api/users/&#123;id&#125; - 获取指定用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUserById</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userService.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * POST /api/users - 创建用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;User&gt; <span class="title function_">createUser</span><span class="params">(<span class="meta">@RequestBody</span> <span class="meta">@Valid</span> User user)</span> &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">saved</span> <span class="operator">=</span> userService.save(user);</span><br><span class="line">        <span class="keyword">return</span> Result.success(saved);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * PUT /api/users/&#123;id&#125; - 更新用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PutMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">updateUser</span><span class="params">(<span class="meta">@PathVariable</span> Long id, </span></span><br><span class="line"><span class="params">                                   <span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line">        user.setId(id);</span><br><span class="line">        userService.update(user);</span><br><span class="line">        <span class="keyword">return</span> Result.success();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * DELETE /api/users/&#123;id&#125; - 删除用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@DeleteMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Void&gt; <span class="title function_">deleteUser</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">        userService.deleteById(id);</span><br><span class="line">        <span class="keyword">return</span> Result.success();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-2-Spring-Cloud-微服务"><a href="#7-2-Spring-Cloud-微服务" class="headerlink" title="7.2 Spring Cloud 微服务"></a>7.2 Spring Cloud 微服务</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["☁️ Spring Cloud 微服务架构"] --> B["网关层\nGateway"]    A --> C["服务注册中心\nNacos/Eureka"]    A --> D["服务调用\nFeign"]    A --> E["配置中心\nConfig"]    A --> F["熔断器\nSentinel"]        B --> B1["路由转发\n权限校验"]    C --> C1["服务注册\n服务发现"]    D --> D1["负载均衡\nHTTP 调用"]    E --> E1["配置管理\n热更新"]    F --> F1["限流\n降级"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml - Spring Boot 配置文件</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">user-service</span>  <span class="comment"># 服务名称</span></span><br><span class="line">  </span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/demo?useSSL=false</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">your_password</span></span><br><span class="line">    <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">  </span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">localhost</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">your_password</span></span><br><span class="line"></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8080</span>  <span class="comment"># 服务端口</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># MyBatis 配置</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath:/mapper/**/*.xml</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">com.example.entity</span></span><br><span class="line">  <span class="attr">configuration:</span></span><br><span class="line">    <span class="attr">map-underscore-to-camel-case:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="7-3-Redis-缓存"><a href="#7-3-Redis-缓存" class="headerlink" title="7.3 Redis 缓存"></a>7.3 Redis 缓存</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Redis 使用示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisCacheService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate redisTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">USER_CACHE_PREFIX</span> <span class="operator">=</span> <span class="string">&quot;user:&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">CACHE_EXPIRE</span> <span class="operator">=</span> <span class="number">3600</span>;  <span class="comment">// 1小时过期</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从缓存获取用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUserFromCache</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> USER_CACHE_PREFIX + userId;</span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> redisTemplate.opsForValue().get(key);</span><br><span class="line">        <span class="keyword">if</span> (json != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> JSON.parseObject(json, User.class);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置用户到缓存</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUserToCache</span><span class="params">(Long userId, User user)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> USER_CACHE_PREFIX + userId;</span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> JSON.toJSONString(user);</span><br><span class="line">        redisTemplate.opsForValue().set(key, json, CACHE_EXPIRE, TimeUnit.SECONDS);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除缓存</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUserCache</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> USER_CACHE_PREFIX + userId;</span><br><span class="line">        redisTemplate.delete(key);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 实现缓存击穿：加锁 + 缓存</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUserWithLock</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> USER_CACHE_PREFIX + userId;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 先查缓存</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> redisTemplate.opsForValue().get(key);</span><br><span class="line">        <span class="keyword">if</span> (json != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> JSON.parseObject(json, User.class);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 缓存未命中，加锁查数据库</span></span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">            <span class="comment">// 双重检查</span></span><br><span class="line">            json = redisTemplate.opsForValue().get(key);</span><br><span class="line">            <span class="keyword">if</span> (json != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> JSON.parseObject(json, User.class);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 从数据库查询</span></span><br><span class="line">            <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userDao.selectById(userId);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 写入缓存</span></span><br><span class="line">            redisTemplate.opsForValue().set(key, </span><br><span class="line">                JSON.toJSONString(user), CACHE_EXPIRE, TimeUnit.SECONDS);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> user;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、第六阶段：微服务与分布式"><a href="#八、第六阶段：微服务与分布式" class="headerlink" title="八、第六阶段：微服务与分布式"></a>八、第六阶段：微服务与分布式</h2><h3 id="8-1-Docker-容器化"><a href="#8-1-Docker-容器化" class="headerlink" title="8.1 Docker 容器化"></a>8.1 Docker 容器化</h3><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Dockerfile - 构建 Docker 镜像</span></span><br><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">17</span>-jdk-slim</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置工作目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制 jar 包</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> target/myapp.jar /app/myapp.jar</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴露端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8080</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行命令</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;java&quot;</span>, <span class="string">&quot;-jar&quot;</span>, <span class="string">&quot;/app/myapp.jar&quot;</span>]</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 常用 Docker 命令</span></span><br><span class="line">docker build -t myapp:1.0 .              <span class="comment"># 构建镜像</span></span><br><span class="line">docker run -p 8080:8080 myapp:1.0         <span class="comment"># 运行容器</span></span><br><span class="line">docker ps                                 <span class="comment"># 查看运行中的容器</span></span><br><span class="line">docker stop myapp                        <span class="comment"># 停止容器</span></span><br><span class="line">docker <span class="built_in">rm</span> myapp                           <span class="comment"># 删除容器</span></span><br><span class="line">docker push myapp:1.0                    <span class="comment"># 推送镜像到仓库</span></span><br></pre></td></tr></table></figure><h3 id="8-2-分布式事务"><a href="#8-2-分布式事务" class="headerlink" title="8.2 分布式事务"></a>8.2 分布式事务</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔄 分布式事务解决方案"] --> B["2PC\n两阶段提交"]    A --> C["TCC\nTry-Confirm-Cancel"]    A --> D["Saga\n saga 模式"]    A --> E["本地消息表\n最终一致性"]        B --> B1["✅ 实现简单\n❌ 性能差\n❌ 数据不一致风险"]        C --> C1["✅ 性能较好\n❌ 实现复杂\n❌ 需要补偿逻辑"]        D --> D1["✅ 高性能\n✅ 可异步执行\n❌ 无隔离性"]        E --> E1["✅ 实现简单\n✅ 最终一致\n❌ 依赖 MQ 可靠性"]        style A fill:#fff3e0</pre></div><h3 id="8-3-消息队列-RabbitMQ"><a href="#8-3-消息队列-RabbitMQ" class="headerlink" title="8.3 消息队列 RabbitMQ"></a>8.3 消息队列 RabbitMQ</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RabbitMQ 生产者示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageProducer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate rabbitTemplate;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">(String routingKey, Object message)</span> &#123;</span><br><span class="line">        rabbitTemplate.convertAndSend(routingKey, message);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发送用户创建消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendUserCreatedMessage</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        Map&lt;String, Object&gt; message = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        message.put(<span class="string">&quot;type&quot;</span>, <span class="string">&quot;USER_CREATED&quot;</span>);</span><br><span class="line">        message.put(<span class="string">&quot;userId&quot;</span>, userId);</span><br><span class="line">        message.put(<span class="string">&quot;timestamp&quot;</span>, System.currentTimeMillis());</span><br><span class="line">        </span><br><span class="line">        rabbitTemplate.convertAndSend(<span class="string">&quot;user.exchange&quot;</span>, <span class="string">&quot;user.created&quot;</span>, message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RabbitMQ 消费者示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageConsumer</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 监听并处理用户创建消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;user.created.queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleUserCreatedMessage</span><span class="params">(Map&lt;String, Object&gt; message)</span> &#123;</span><br><span class="line">        <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> Long.valueOf(message.get(<span class="string">&quot;userId&quot;</span>).toString());</span><br><span class="line">        System.out.println(<span class="string">&quot;收到用户创建消息：&quot;</span> + userId);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 执行业务逻辑：发送欢迎邮件、初始化用户数据等</span></span><br><span class="line">        welcomeEmailService.send(userId);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、第七阶段：DevOps-与工具链"><a href="#九、第七阶段：DevOps-与工具链" class="headerlink" title="九、第七阶段：DevOps 与工具链"></a>九、第七阶段：DevOps 与工具链</h2><h3 id="9-1-Git-版本控制"><a href="#9-1-Git-版本控制" class="headerlink" title="9.1 Git 版本控制"></a>9.1 Git 版本控制</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Git 基础命令</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化仓库</span></span><br><span class="line">git init</span><br><span class="line"></span><br><span class="line"><span class="comment"># 克隆远程仓库</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/example/repo.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加文件到暂存区</span></span><br><span class="line">git add .                    <span class="comment"># 添加所有文件</span></span><br><span class="line">git add file.txt            <span class="comment"># 添加指定文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 提交到本地仓库</span></span><br><span class="line">git commit -m <span class="string">&quot;提交说明&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 推送到远程仓库</span></span><br><span class="line">git push origin main</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拉取远程更新</span></span><br><span class="line">git pull origin main</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建并切换分支</span></span><br><span class="line">git checkout -b feature/new-feature</span><br><span class="line"></span><br><span class="line"><span class="comment"># 合并分支</span></span><br><span class="line">git merge feature/new-feature</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line">git status</span><br><span class="line">git <span class="built_in">log</span> --oneline --graph</span><br></pre></td></tr></table></figure><h3 id="9-2-Maven-项目构建"><a href="#9-2-Maven-项目构建" class="headerlink" title="9.2 Maven 项目构建"></a>9.2 Maven 项目构建</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- pom.xml 示例 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.example<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>demo-project<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>17<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mybatis-plus.version</span>&gt;</span>3.5.5<span class="tag">&lt;/<span class="name">mybatis-plus.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.baomidou<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-plus-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;mybatis-plus.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="9-3-Linux-基础命令"><a href="#9-3-Linux-基础命令" class="headerlink" title="9.3 Linux 基础命令"></a>9.3 Linux 基础命令</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Linux 常用命令</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件操作</span></span><br><span class="line"><span class="built_in">ls</span> -la                      <span class="comment"># 查看目录详情</span></span><br><span class="line"><span class="built_in">cd</span> /path/to/dir            <span class="comment"># 切换目录</span></span><br><span class="line"><span class="built_in">pwd</span>                         <span class="comment"># 显示当前目录</span></span><br><span class="line"><span class="built_in">mkdir</span> newdir               <span class="comment"># 创建目录</span></span><br><span class="line"><span class="built_in">rm</span> -rf filename            <span class="comment"># 删除文件/目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文本编辑</span></span><br><span class="line"><span class="built_in">cat</span> file.txt               <span class="comment"># 查看文件内容</span></span><br><span class="line">grep <span class="string">&quot;keyword&quot;</span> file.txt   <span class="comment"># 搜索关键字</span></span><br><span class="line">vim file.txt              <span class="comment"># 编辑文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 进程与资源</span></span><br><span class="line">ps aux | grep java         <span class="comment"># 查看 Java 进程</span></span><br><span class="line">top                        <span class="comment"># 查看系统资源</span></span><br><span class="line"><span class="built_in">kill</span> -9 pid               <span class="comment"># 强制终止进程</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 网络操作</span></span><br><span class="line">curl http://example.com   <span class="comment"># 发送 HTTP 请求</span></span><br><span class="line">netstat -tlnp | grep 8080 <span class="comment"># 查看端口占用</span></span><br><span class="line">ping example.com          <span class="comment"># 测试网络连通</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Docker 操作</span></span><br><span class="line">docker ps                <span class="comment"># 查看容器</span></span><br><span class="line">docker logs -f container  <span class="comment"># 查看日志</span></span><br><span class="line">docker <span class="built_in">exec</span> -it container bash  <span class="comment"># 进入容器</span></span><br></pre></td></tr></table></figure><hr><h2 id="十、学习方法与资源推荐"><a href="#十、学习方法与资源推荐" class="headerlink" title="十、学习方法与资源推荐"></a>十、学习方法与资源推荐</h2><h3 id="10-1-正确的学习方法"><a href="#10-1-正确的学习方法" class="headerlink" title="10.1 正确的学习方法"></a>10.1 正确的学习方法</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📚 正确的学习方法"] --> B["📖 夯实基础"]    A --> C["💻 多敲代码"]    A --> D["🔍 理解原理"]    A --> E["📝 做好笔记"]    A --> F["🤝 交流分享"]        B --> B1["不要急于求成\n基础决定高度"]        C --> C1["光学不练假把式\n项目驱动学习"]        D --> D1["知其然\n知其所以然"]        E --> E1["博客记录\n知识沉淀"]        F --> F1["加入社区\n相互学习"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0</pre></div><h3 id="10-2-推荐学习资源"><a href="#10-2-推荐学习资源" class="headerlink" title="10.2 推荐学习资源"></a>10.2 推荐学习资源</h3><table><thead><tr><th>资源类型</th><th>推荐内容</th><th>适用阶段</th></tr></thead><tbody><tr><td><strong>书籍</strong></td><td>《Java 核心技术 卷 I》《深入理解 Java 虚拟机》《Effective Java》</td><td>全阶段</td></tr><tr><td><strong>在线课程</strong></td><td>极客时间、拉勾教育、B 站视频</td><td>全阶段</td></tr><tr><td><strong>官方文档</strong></td><td>Spring、Docker、MyBatis 官方文档</td><td>进阶</td></tr><tr><td><strong>技术社区</strong></td><td>GitHub、掘金、思否、博客园</td><td>全阶段</td></tr><tr><td><strong>刷题平台</strong></td><td>LeetCode、牛客网</td><td>算法、面试</td></tr></tbody></table><h3 id="10-3-阶段性项目推荐"><a href="#10-3-阶段性项目推荐" class="headerlink" title="10.3 阶段性项目推荐"></a>10.3 阶段性项目推荐</h3><table><thead><tr><th>阶段</th><th>项目类型</th><th>推荐项目</th></tr></thead><tbody><tr><td><strong>基础阶段</strong></td><td>控制台应用</td><td>图书管理系统、学生成绩管理</td></tr><tr><td><strong>进阶阶段</strong></td><td>Web 小应用</td><td>个人博客、在线笔记</td></tr><tr><td><strong>框架阶段</strong></td><td>完整 Web 系统</td><td>电商后台管理系统</td></tr><tr><td><strong>微服务阶段</strong></td><td>分布式系统</td><td>秒杀系统、即时通讯</td></tr></tbody></table><hr><h2 id="十一、常见问题-FAQ"><a href="#十一、常见问题-FAQ" class="headerlink" title="十一、常见问题 FAQ"></a>十一、常见问题 FAQ</h2><h3 id="Q1：Java-和-Python-应该选哪个？"><a href="#Q1：Java-和-Python-应该选哪个？" class="headerlink" title="Q1：Java 和 Python 应该选哪个？"></a>Q1：Java 和 Python 应该选哪个？</h3><p><strong>A：</strong> 从就业角度，Java 需求量大、企业级应用广泛；从学习角度，Python 上手快、科学计算强。<strong>建议</strong>：如果目标是后端开发就业，选择 Java；如果是数据分析&#x2F;AI 领域，选择 Python。</p><h3 id="Q2：学完基础需要多久才能找到工作？"><a href="#Q2：学完基础需要多久才能找到工作？" class="headerlink" title="Q2：学完基础需要多久才能找到工作？"></a>Q2：学完基础需要多久才能找到工作？</h3><p><strong>A：</strong> 一般来说，系统学习 4-6 个月可以具备初级开发能力，6-9 个月可以尝试找中高级岗位。但具体时间因人而异，<strong>关键是多做项目、积累经验</strong>。</p><h3 id="Q3：要不要学-Kotlin？"><a href="#Q3：要不要学-Kotlin？" class="headerlink" title="Q3：要不要学 Kotlin？"></a>Q3：要不要学 Kotlin？</h3><p><strong>A：</strong> Kotlin 是 JVM 语言，与 Java 完全兼容，Android 开发首选。<strong>建议</strong>：先学好 Java 打牢基础，有余力再学 Kotlin。</p><h3 id="Q4：微服务要不要学？"><a href="#Q4：微服务要不要学？" class="headerlink" title="Q4：微服务要不要学？"></a>Q4：微服务要不要学？</h3><p><strong>A：</strong> 微服务是企业级开发的主流架构，是中高级开发者的必备技能。建议<strong>先学好 Spring Boot 基础</strong>，再学习 Spring Cloud 微服务。</p><h3 id="Q5：如何准备面试？"><a href="#Q5：如何准备面试？" class="headerlink" title="Q5：如何准备面试？"></a>Q5：如何准备面试？</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🎯 面试准备清单"] --> B["📚 基础知识"]    A --> C["💻 项目经验"]    A --> D["🔧 工具使用"]    A --> E["🧠 算法逻辑"]        B --> B1["Java 基础\n集合源码\n多线程\nJVM"]        C --> C1["项目亮点\n难点解决\n技术选型"]        D --> D1["Git\nMaven\nLinux\nDocker"]        E --> E1["数组\n链表\n树\n排序"]        style A fill:#fff3e0</pre></div><hr><h2 id="十二、总结"><a href="#十二、总结" class="headerlink" title="十二、总结"></a>十二、总结</h2><h3 id="12-1-学习路线总结"><a href="#12-1-学习路线总结" class="headerlink" title="12.1 学习路线总结"></a>12.1 学习路线总结</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Java 学习路线))    第一阶段      Java 环境搭建      基本语法      面向对象      异常处理    第二阶段      集合框架      多线程      I/O 流      JVM 原理    第三阶段      MySQL      JDBC      MyBatis      Redis    第四阶段      HTML/CSS/JS      Servlet/JSP      HTTP 协议    第五阶段      Spring Boot      Spring MVC      Spring Cloud      Redis 缓存    第六阶段      微服务架构      Docker      MQ 消息队列      Linux    第七阶段      Git 版本控制      Maven 构建      CI/CD 部署      性能调优</pre></div><h3 id="12-2-心态建议"><a href="#12-2-心态建议" class="headerlink" title="12.2 心态建议"></a>12.2 心态建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["💪 学习心态"] --> B["🌱 循序渐进"]    A --> C["🔥 保持热情"]    A --> D["🔄 持续学习"]    A --> E["💡 独立思考"]        B --> B1["不要急于求成\n基础决定高度"]        C --> C1["遇到困难不退缩\n兴趣是最好的老师"]        D --> D1["技术更新快\n保持学习热情"]        E --> E1["多问为什么\n理解原理而非背答案"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0</pre></div><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Java 学习是一场马拉松，不是短跑。每个人的起点和速度都不同，重要的是保持专注和坚持。不要被眼前的困难吓倒，也不要被别人的进度影响节奏。认准方向，一步一个脚印，你一定能到达目的地！加油！💪</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/java-learning-path/</id>
    <link href="https://blog.codenav.top/java-learning-path/"/>
    <published>2026-05-24T05:43:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Java-学习路线完整指南：从入门到就业-🌟"><a href="#Java-学习路线完整指南：从入门到就业-🌟" class="headerlink" title="Java 学习路线完整指南：从入门到就业 🌟"></a>Java 学习路线完整指南：从入门]]>
    </summary>
    <title>Java 学习路线完整指南：从入门到就业 🌟</title>
    <updated>2026-05-24T05:44:22.982Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Hibernate" scheme="https://blog.codenav.top/tags/Hibernate/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="ORM" scheme="https://blog.codenav.top/tags/ORM/"/>
    <category term="持久层" scheme="https://blog.codenav.top/tags/%E6%8C%81%E4%B9%85%E5%B1%82/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <content>
      <![CDATA[<h1 id="Hibernate-框架从入门到实战：全自动化-ORM-的力量-🔮"><a href="#Hibernate-框架从入门到实战：全自动化-ORM-的力量-🔮" class="headerlink" title="Hibernate 框架从入门到实战：全自动化 ORM 的力量 🔮"></a>Hibernate 框架从入门到实战：全自动化 ORM 的力量 🔮</h1><blockquote><p>Hibernate 是 Java 领域最经典的全自动 ORM（对象关系映射）框架之一，它让开发者彻底告别繁琐的 JDBC 代码，通过对象操作数据库。本文将系统讲解 Hibernate 的核心概念、映射配置、 CRUD 操作、事务管理、HQL 查询语言以及与 Spring 的集成，帮助你全面掌握这门传统但依然重要的持久层技术！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80hibernate-%E6%A6%82%E8%BF%B0%E4%BB%80%E4%B9%88%E6%98%AF-orm">一、Hibernate 概述：什么是 ORM？</a></li><li><a href="#%E4%BA%8C%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E9%85%8D%E7%BD%AE">二、环境搭建与配置</a></li><li><a href="#%E4%B8%89%E5%AE%9E%E4%BD%93%E6%98%A0%E5%B0%84%E8%AF%A6%E8%A7%A3">三、实体映射详解</a></li><li><a href="#%E5%9B%9Bhibernate-%E6%A0%B8%E5%BF%83-api">四、Hibernate 核心 API</a></li><li><a href="#%E4%BA%94crud-%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E6%93%8D%E4%BD%9C">五、CRUD 增删改查操作</a></li><li><a href="#%E5%85%AD%E5%AF%B9%E8%B1%A1%E7%8A%B6%E6%80%81%E4%B8%8E%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98">六、对象状态与一级缓存</a></li><li><a href="#%E4%B8%83%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BB%E9%85%8D%E7%BD%AE">七、映射关系配置</a></li><li><a href="#%E5%85%ABhql-%E6%9F%A5%E8%AF%A2%E8%AF%AD%E8%A8%80">八、HQL 查询语言</a></li><li><a href="#%E4%B9%9D%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86">九、事务管理</a></li><li><a href="#%E5%8D%81%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD%E4%B8%8E%E6%8A%93%E5%8F%96%E7%AD%96%E7%95%A5">十、延迟加载与抓取策略</a></li><li><a href="#%E5%8D%81%E4%B8%80%E4%B8%8E-spring-%E9%9B%86%E6%88%90">十一、与 Spring 集成</a></li><li><a href="#%E5%8D%81%E4%BA%8C%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十二、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%B8%89%E6%80%BB%E7%BB%93">十三、总结</a></li></ul><hr><h2 id="一、Hibernate-概述：什么是-ORM？"><a href="#一、Hibernate-概述：什么是-ORM？" class="headerlink" title="一、Hibernate 概述：什么是 ORM？"></a>一、Hibernate 概述：什么是 ORM？</h2><h3 id="1-1-从-JDBC-到-ORM-的进化"><a href="#1-1-从-JDBC-到-ORM-的进化" class="headerlink" title="1.1 从 JDBC 到 ORM 的进化"></a>1.1 从 JDBC 到 ORM 的进化</h3><p>在 JDBC 时代，我们操作数据库是这样的：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["☕️ Java 对象"] -->|"手动映射"| B["📝 SQL 语句"]    B -->|"JDBC 执行"| C["🗄️ 数据库表"]        style A fill:#e3f2fd    style B fill:#fff3e0    style C fill:#f8bbd0</pre></div><ul><li>❌ 需要手动编写 SQL 语句</li><li>❌ 需要手动从 ResultSet 映射到 Java 对象</li><li>❌ 需要手动管理数据库连接和资源</li><li>❌ SQL 语句散落在代码各处，难以维护</li></ul><p>而 ORM 的目标是：<strong>让对象和表之间自动映射，你只需要操作对象即可。</strong></p><h3 id="1-2-Hibernate-是什么？"><a href="#1-2-Hibernate-是什么？" class="headerlink" title="1.2 Hibernate 是什么？"></a>1.2 Hibernate 是什么？</h3><p>Hibernate 是一个全自动的 ORM 框架，它实现了 Java 对象与数据库表之间的自动映射：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["🧍 User 对象"] -->|"save()/update()"| B["📦 Hibernate ORM"]    B -->|"自动转换"| C["🗄️ users 表"]        A -->|"findById()"| B    B -->|"自动映射"| C        style A fill:#c8e6c9    style B fill:#e3f2fd    style C fill:#f8bbd0</pre></div><p><strong>Hibernate 的核心优势</strong>：</p><ul><li>✅ <strong>全自动映射</strong>：Java 对象 ↔ 数据库表，自动转换</li><li>✅ <strong>无需编写 SQL</strong>：框架自动生成并执行 SQL</li><li>✅ <strong>数据库无关性</strong>：切换数据库只需修改配置</li><li>✅ <strong>缓存支持</strong>：内置一级缓存和二级缓存，提升性能</li><li>✅ <strong>事务管理</strong>：提供声明式事务支持</li><li>✅ <strong>丰富查询</strong>：HQL、QBC、原生 SQL 等多种查询方式</li></ul><h3 id="1-3-Hibernate-vs-MyBatis-对比"><a href="#1-3-Hibernate-vs-MyBatis-对比" class="headerlink" title="1.3 Hibernate vs MyBatis 对比"></a>1.3 Hibernate vs MyBatis 对比</h3><table><thead><tr><th>对比维度</th><th>Hibernate</th><th>MyBatis</th></tr></thead><tbody><tr><td><strong>映射方式</strong></td><td>全自动</td><td>半自动</td></tr><tr><td><strong>SQL 控制</strong></td><td>框架自动生成</td><td>手动编写</td></tr><tr><td><strong>学习曲线</strong></td><td>较陡峭</td><td>平缓</td></tr><tr><td><strong>灵活性</strong></td><td>较低（但足够大多数场景）</td><td>高（完全可控）</td></tr><tr><td><strong>性能调优</strong></td><td>需要深入理解框架</td><td>直接优化 SQL</td></tr><tr><td><strong>适用场景</strong></td><td>业务简单、表结构稳定</td><td>复杂查询、性能敏感</td></tr><tr><td><strong>国内市场</strong></td><td>逐渐减少</td><td>绝对主流（+ MyBatis-Plus）</td></tr></tbody></table><blockquote><p>💡 <strong>国内现状</strong>：MyBatis 因其灵活性和可控性在国内占据主流，但 Hibernate 在企业级应用中仍有广泛应用，特别是在传统行业（如金融、银行）中。选择框架需要根据项目实际需求决定，两者都是值得掌握的技能。</p></blockquote><hr><h2 id="二、环境搭建与配置"><a href="#二、环境搭建与配置" class="headerlink" title="二、环境搭建与配置"></a>二、环境搭建与配置</h2><h3 id="2-1-Maven-依赖"><a href="#2-1-Maven-依赖" class="headerlink" title="2.1 Maven 依赖"></a>2.1 Maven 依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">project</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.example<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hibernate-demo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">hibernate.version</span>&gt;</span>6.3.1.Final<span class="tag">&lt;/<span class="name">hibernate.version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mysql.version</span>&gt;</span>8.0.33<span class="tag">&lt;/<span class="name">mysql.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- Hibernate 核心依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.hibernate<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hibernate-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;hibernate.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- Hibernate JPA 实现（包含 Hibernate EntityManager） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>jakarta.persistence<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jakarta.persistence-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- MySQL 驱动 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-j<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;mysql.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 数据源 HikariCP（连接池） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.zaxxer<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>HikariCP<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>5.0.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- JUnit 测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.13.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- Lombok --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.18.30<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 日志 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.9<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-simple<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.9<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-2-创建数据库表"><a href="#2-2-创建数据库表" class="headerlink" title="2.2 创建数据库表"></a>2.2 创建数据库表</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> DATABASE IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> hibernate_demo </span><br><span class="line"><span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8mb4_unicode_ci;</span><br><span class="line"></span><br><span class="line">USE hibernate_demo;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 用户表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> users (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    username <span class="type">VARCHAR</span>(<span class="number">50</span>) <span class="keyword">NOT NULL</span> <span class="keyword">UNIQUE</span>,</span><br><span class="line">    password <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">    email <span class="type">VARCHAR</span>(<span class="number">100</span>),</span><br><span class="line">    status <span class="type">INT</span> <span class="keyword">DEFAULT</span> <span class="number">1</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    updated_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 文章表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> articles (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    user_id <span class="type">BIGINT</span> <span class="keyword">NOT NULL</span>,</span><br><span class="line">    title <span class="type">VARCHAR</span>(<span class="number">200</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">    content TEXT,</span><br><span class="line">    views <span class="type">INT</span> <span class="keyword">DEFAULT</span> <span class="number">0</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    updated_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    <span class="keyword">FOREIGN KEY</span> (user_id) <span class="keyword">REFERENCES</span> users(id) <span class="keyword">ON</span> <span class="keyword">DELETE</span> CASCADE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 插入测试数据</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users (username, password, email) <span class="keyword">VALUES</span></span><br><span class="line">(<span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;pass123&#x27;</span>, <span class="string">&#x27;admin@example.com&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;test&#x27;</span>, <span class="string">&#x27;pass456&#x27;</span>, <span class="string">&#x27;test@example.com&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT INTO</span> articles (user_id, title, content, views) <span class="keyword">VALUES</span></span><br><span class="line">(<span class="number">1</span>, <span class="string">&#x27;Hibernate 入门&#x27;</span>, <span class="string">&#x27;Hibernate 是一个优秀的 ORM 框架...&#x27;</span>, <span class="number">100</span>),</span><br><span class="line">(<span class="number">1</span>, <span class="string">&#x27;Hibernate 进阶&#x27;</span>, <span class="string">&#x27;本文讲解 Hibernate 的高级特性...&#x27;</span>, <span class="number">200</span>),</span><br><span class="line">(<span class="number">2</span>, <span class="string">&#x27;JPA 规范简介&#x27;</span>, <span class="string">&#x27;JPA 是 Java 持久化 API 规范...&#x27;</span>, <span class="number">150</span>);</span><br></pre></td></tr></table></figure><h3 id="2-3-hibernate-cfg-xml-全局配置"><a href="#2-3-hibernate-cfg-xml-全局配置" class="headerlink" title="2.3 hibernate.cfg.xml 全局配置"></a>2.3 hibernate.cfg.xml 全局配置</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">hibernate-configuration</span> <span class="keyword">PUBLIC</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;-//Hibernate/Hibernate Configuration DTD 3.0//EN&quot;</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;http://hibernate.org/dtd/hibernate-configuration-3.0.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">hibernate-configuration</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- SessionFactory：整个应用共享一个 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">session-factory</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ========== 数据库连接配置 ========== --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection.driver_class&quot;</span>&gt;</span>com.mysql.cj.jdbc.Driver<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection.url&quot;</span>&gt;</span></span><br><span class="line">            jdbc:mysql://localhost:3306/hibernate_demo?useSSL=false<span class="symbol">&amp;amp;</span>serverTimezone=Asia/Shanghai<span class="symbol">&amp;amp;</span>characterEncoding=utf8</span><br><span class="line">        <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection.username&quot;</span>&gt;</span>root<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection.password&quot;</span>&gt;</span>your_password<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 连接池配置（HikariCP） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection.pool_size&quot;</span>&gt;</span>10<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection HikariCP minimum-idle&quot;</span>&gt;</span>5<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection HikariCP maximum-pool-size&quot;</span>&gt;</span>20<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ========== SQL 方言与格式 ========== --&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 指定数据库方言：Hibernate 会根据方言生成兼容的 SQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.dialect&quot;</span>&gt;</span>org.hibernate.dialect.MySQLDialect<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 是否打印 SQL 语句（调试用） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.show_sql&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 是否格式化 SQL（美化输出） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.format_sql&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 是否在控制台打印建表 SQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.hbm2ddl.auto&quot;</span>&gt;</span>update<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">            hibernate.hbm2ddl.auto 可选值：</span></span><br><span class="line"><span class="comment">            - none：不执行任何操作（默认值）</span></span><br><span class="line"><span class="comment">            - create：每次启动创建新表（会删除旧数据）</span></span><br><span class="line"><span class="comment">            - create-drop：每次启动创建，关闭删除</span></span><br><span class="line"><span class="comment">            - update：更新表结构（推荐开发使用）</span></span><br><span class="line"><span class="comment">            - validate：校验映射与表结构是否一致</span></span><br><span class="line"><span class="comment">        --&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ========== 事务配置 ========== --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.transaction.factory_class&quot;</span>&gt;</span></span><br><span class="line">            org.hibernate.transaction.JDBCTransactionFactory</span><br><span class="line">        <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ========== 缓存配置 ========== --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.cache.use_second_level_cache&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.cache.region.factory_class&quot;</span>&gt;</span></span><br><span class="line">            org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</span><br><span class="line">        <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ========== 当前会话上下文 ========== --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.current_session_context_class&quot;</span>&gt;</span>thread<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 可选：thread（线程本地）、jta（分布式事务）--&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ========== 实体映射文件 ========== --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mapping</span> <span class="attr">resource</span>=<span class="string">&quot;com/example/entity/User.hbm.xml&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mapping</span> <span class="attr">resource</span>=<span class="string">&quot;com/example/entity/Article.hbm.xml&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 或使用注解方式（扫描包） --&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- &lt;mapping class=&quot;com.example.entity.User&quot;/&gt; --&gt;</span></span><br><span class="line">        </span><br><span class="line">    <span class="tag">&lt;/<span class="name">session-factory</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">hibernate-configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-4-HibernateUtil-工具类"><a href="#2-4-HibernateUtil-工具类" class="headerlink" title="2.4 HibernateUtil 工具类"></a>2.4 HibernateUtil 工具类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.util;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.hibernate.Session;</span><br><span class="line"><span class="keyword">import</span> org.hibernate.SessionFactory;</span><br><span class="line"><span class="keyword">import</span> org.hibernate.cfg.Configuration;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Hibernate 工具类：管理 SessionFactory</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HibernateUtil</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SessionFactory：整个应用只需要一个，轻量级且线程安全</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> SessionFactory sessionFactory;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 加载 hibernate.cfg.xml 配置</span></span><br><span class="line">            <span class="type">Configuration</span> <span class="variable">configuration</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Configuration</span>();</span><br><span class="line">            configuration.configure(<span class="string">&quot;hibernate.cfg.xml&quot;</span>);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 构建 SessionFactory（耗时长，通常只创建一次）</span></span><br><span class="line">            sessionFactory = configuration.buildSessionFactory();</span><br><span class="line">            </span><br><span class="line">            System.out.println(<span class="string">&quot;✅ SessionFactory 初始化成功&quot;</span>);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;初始化 SessionFactory 失败&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取 SessionFactory</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> SessionFactory <span class="title function_">getSessionFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sessionFactory;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取 Session</span></span><br><span class="line"><span class="comment">     * Session 是应用程序与数据库的会话，每次操作创建一个</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Session <span class="title function_">openSession</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sessionFactory.openSession();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取当前 Session（线程绑定，无需手动关闭）</span></span><br><span class="line"><span class="comment">     * 配合 getCurrentSession 使用，实现事务会话管理</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Session <span class="title function_">getCurrentSession</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sessionFactory.getCurrentSession();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 关闭 SessionFactory（应用停止时调用）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">shutdown</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (sessionFactory != <span class="literal">null</span> &amp;&amp; !sessionFactory.isClosed()) &#123;</span><br><span class="line">            sessionFactory.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="三、实体映射详解"><a href="#三、实体映射详解" class="headerlink" title="三、实体映射详解"></a>三、实体映射详解</h2><h3 id="3-1-实体类设计"><a href="#3-1-实体类设计" class="headerlink" title="3.1 实体类设计"></a>3.1 实体类设计</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"><span class="keyword">import</span> lombok.NoArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.AllArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.Builder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Builder</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="keyword">private</span> Date createdAt;</span><br><span class="line">    <span class="keyword">private</span> Date updatedAt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-XML-映射文件（User-hbm-xml）"><a href="#3-2-XML-映射文件（User-hbm-xml）" class="headerlink" title="3.2 XML 映射文件（User.hbm.xml）"></a>3.2 XML 映射文件（User.hbm.xml）</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">hibernate-mapping</span> <span class="keyword">PUBLIC</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;-//Hibernate/Hibernate Mapping DTD 3.0//EN&quot;</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;http://hibernate.org/dtd/hibernate-mapping-3.0.dtd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">hibernate-mapping</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- class 标签：映射类与表 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">class</span> <span class="attr">name</span>=<span class="string">&quot;com.example.entity.User&quot;</span> <span class="attr">table</span>=<span class="string">&quot;users&quot;</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- id 标签：映射主键 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">name</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 主键生成策略 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">generator</span> <span class="attr">class</span>=<span class="string">&quot;native&quot;</span>/&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">                常见主键生成策略：</span></span><br><span class="line"><span class="comment">                - native：数据库自增（MySQL 用 AUTO_INCREMENT）</span></span><br><span class="line"><span class="comment">                - identity：支持自增的数据库</span></span><br><span class="line"><span class="comment">                - sequence：序列（Oracle、PostgreSQL）</span></span><br><span class="line"><span class="comment">                - uuid：UUID 算法生成唯一字符串</span></span><br><span class="line"><span class="comment">                - assigned：手动指定 ID</span></span><br><span class="line"><span class="comment">                - increment：Hibernate 自增（不推荐集群环境）</span></span><br><span class="line"><span class="comment">            --&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- property 标签：映射普通属性 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span> <span class="attr">not-null</span>=<span class="string">&quot;true&quot;</span> <span class="attr">unique</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">column</span>=<span class="string">&quot;password&quot;</span> <span class="attr">not-null</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 类型推断：Hibernate 会自动根据 Java 类型推断 SQL 类型 --&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 整数的默认值就是 NOT NULL，需要显式指定 nullable --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;status&quot;</span> <span class="attr">column</span>=<span class="string">&quot;status&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">column</span> <span class="attr">name</span>=<span class="string">&quot;status&quot;</span> <span class="attr">default</span>=<span class="string">&quot;1&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 日期类型映射 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;createdAt&quot;</span> <span class="attr">column</span>=<span class="string">&quot;created_at&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;updatedAt&quot;</span> <span class="attr">column</span>=<span class="string">&quot;updated_at&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">    <span class="tag">&lt;/<span class="name">class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">hibernate-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-3-JPA-注解映射（推荐方式）"><a href="#3-3-JPA-注解映射（推荐方式）" class="headerlink" title="3.3 JPA 注解映射（推荐方式）"></a>3.3 JPA 注解映射（推荐方式）</h3><p>相比 XML 配置，注解方式更加简洁，是目前主流的选择：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jakarta.persistence.*;</span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"><span class="keyword">import</span> lombok.NoArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.AllArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.Builder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户实体类（JPA 注解方式）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span>                 <span class="comment">// 声明为实体类</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span> <span class="comment">// 映射到哪张表</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Builder</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span>                                 <span class="comment">// 主键</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span>  <span class="comment">// 自增策略</span></span><br><span class="line">    <span class="meta">@Column(name = &quot;id&quot;)</span>                <span class="comment">// 映射到哪一列</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;username&quot;, nullable = false, unique = true)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;password&quot;, nullable = false)</span></span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;email&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;status&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;created_at&quot;)</span></span><br><span class="line">    <span class="meta">@Temporal(TemporalType.TIMESTAMP)</span>   <span class="comment">// 日期时间类型</span></span><br><span class="line">    <span class="keyword">private</span> Date createdAt;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;updated_at&quot;)</span></span><br><span class="line">    <span class="meta">@Temporal(TemporalType.TIMESTAMP)</span></span><br><span class="line">    <span class="keyword">private</span> Date updatedAt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-主键生成策略"><a href="#3-4-主键生成策略" class="headerlink" title="3.4 主键生成策略"></a>3.4 主键生成策略</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["主键生成策略"] --> B["IDENTITY\n自增"]    A --> C["SEQUENCE\n序列"]    A --> D["TABLE\n表生成"]    A --> E["UUID\n全局唯一"]    A --> F["AUTO\n自动选择"]        B --> B1["MySQL AUTO_INCREMENT"]    C --> C1["Oracle SEQUENCE"]    D --> D1["Hibernate 序列表"]    E --> E1["分布式环境"]    F --> F1["根据方言自动选择"]        style A fill:#fff3e0    style B fill:#c8e6c9    style C fill:#e3f2fd    style D fill:#fff3e0    style E fill:#f8bbd0    style F fill:#e3f2fd</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 自增（MySQL 推荐）</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line"><span class="keyword">private</span> Long id;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 序列（Oracle、PostgreSQL 推荐）</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = &quot;user_seq&quot;)</span></span><br><span class="line"><span class="meta">@SequenceGenerator(name = &quot;user_seq&quot;, sequenceName = &quot;user_sequence&quot;, allocationSize = 1)</span></span><br><span class="line"><span class="keyword">private</span> Long id;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. TABLE（跨数据库，但性能差）</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue(strategy = GenerationType.TABLE, generator = &quot;id_gen&quot;)</span></span><br><span class="line"><span class="meta">@TableGenerator(name = &quot;id_gen&quot;, table = &quot;hibernate_sequences&quot;,</span></span><br><span class="line"><span class="meta">               pkColumnName = &quot;sequence_name&quot;, valueColumnName = &quot;next_val&quot;)</span></span><br><span class="line"><span class="keyword">private</span> Long id;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. UUID（分布式环境首选）</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue(strategy = GenerationType.UUID)</span></span><br><span class="line"><span class="keyword">private</span> String id;  <span class="comment">// 使用 String 类型</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. AUTO（根据方言自动选择）</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue(strategy = GenerationType.AUTO)</span></span><br><span class="line"><span class="keyword">private</span> Long id;</span><br></pre></td></tr></table></figure><h3 id="3-5-属性映射细节"><a href="#3-5-属性映射细节" class="headerlink" title="3.5 属性映射细节"></a>3.5 属性映射细节</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 基本映射（列名默认驼峰转下划线）</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 指定列名和约束</span></span><br><span class="line">    <span class="meta">@Column(name = &quot;user_name&quot;, nullable = false, length = 50, unique = true)</span></span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 插入/更新时忽略字段（不会被保存到数据库）</span></span><br><span class="line">    <span class="meta">@Transient</span></span><br><span class="line">    <span class="keyword">private</span> String temporaryField;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 枚举类型映射（默认存枚举名）</span></span><br><span class="line">    <span class="meta">@Enumerated(EnumType.STRING)</span>  <span class="comment">// 或 EnumType.ORDINAL 存序号</span></span><br><span class="line">    <span class="keyword">private</span> UserStatus status;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 大字段映射</span></span><br><span class="line">    <span class="meta">@Lob</span></span><br><span class="line">    <span class="keyword">private</span> String description;  <span class="comment">// TEXT 类型</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Lob</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">byte</span>[] avatar;      <span class="comment">// BLOB 类型</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 时间类型</span></span><br><span class="line">    <span class="meta">@Temporal(TemporalType.DATE)</span>       <span class="comment">// 只存日期</span></span><br><span class="line">    <span class="meta">@Temporal(TemporalType.TIME)</span>        <span class="comment">// 只存时间</span></span><br><span class="line">    <span class="meta">@Temporal(TemporalType.TIMESTAMP)</span>   <span class="comment">// 日期时间（默认）</span></span><br><span class="line">    <span class="keyword">private</span> Date birthDate;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 枚举类</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">UserStatus</span> &#123;</span><br><span class="line">    ACTIVE,     <span class="comment">// 存字符串 &quot;ACTIVE&quot;（@Enumerated(EnumType.STRING)）</span></span><br><span class="line">    INACTIVE,   <span class="comment">// 或存序号 0, 1, 2（@Enumerated(EnumType.ORDINAL)）</span></span><br><span class="line">    DELETED</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、Hibernate-核心-API"><a href="#四、Hibernate-核心-API" class="headerlink" title="四、Hibernate 核心 API"></a>四、Hibernate 核心 API</h2><h3 id="4-1-核心对象关系"><a href="#4-1-核心对象关系" class="headerlink" title="4.1 核心对象关系"></a>4.1 核心对象关系</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔧 Hibernate 核心 API"] --> B["📋 Configuration"]    A --> C["🏭 SessionFactory"]    A --> D["📦 Session"]    A --> E["🔒 Transaction"]        B -->|"buildSessionFactory()"| C    C -->|"openSession() / getCurrentSession()"| D    D -->|"beginTransaction()"| E        style A fill:#fff3e0    style C fill:#c8e6c9    style D fill:#c8e6c9    style E fill:#f8bbd0</pre></div><table><thead><tr><th>API</th><th>说明</th><th>生命周期</th></tr></thead><tbody><tr><td><strong>Configuration</strong></td><td>加载配置，构建 SessionFactory</td><td>应用级别</td></tr><tr><td><strong>SessionFactory</strong></td><td>创建 Session，重量级，线程安全</td><td>应用级别</td></tr><tr><td><strong>Session</strong></td><td>数据库会话，操作 CRUD</td><td>请求级别</td></tr><tr><td><strong>Transaction</strong></td><td>事务管理</td><td>业务方法级别</td></tr><tr><td><strong>Query</strong></td><td>执行 HQL 查询</td><td>查询执行完即可关闭</td></tr><tr><td><strong>Criteria</strong></td><td>面向对象查询</td><td>查询执行完即可关闭</td></tr></tbody></table><h3 id="4-2-Session-核心方法"><a href="#4-2-Session-核心方法" class="headerlink" title="4.2 Session 核心方法"></a>4.2 Session 核心方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Session 接口常用方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ===== 增删改 =====</span></span><br><span class="line">session.save(entity);           <span class="comment">// 保存（返回主键）</span></span><br><span class="line">session.persist(entity);       <span class="comment">// 持久化（无返回值，void）</span></span><br><span class="line">session.update(entity);          <span class="comment">// 更新</span></span><br><span class="line">session.saveOrUpdate(entity);  <span class="comment">// 保存或更新（根据主键判断）</span></span><br><span class="line">session.delete(entity);         <span class="comment">// 删除</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ===== 查询 =====</span></span><br><span class="line">session.get(Class, id);        <span class="comment">// 根据主键查询（立即加载）</span></span><br><span class="line">session.load(Class, id);       <span class="comment">// 根据主键查询（延迟加载）</span></span><br><span class="line">session.createQuery(hql);      <span class="comment">// HQL 查询</span></span><br><span class="line">session.createCriteria(Class); <span class="comment">// QBC 查询</span></span><br><span class="line">session.createSQLQuery(sql);   <span class="comment">// 原生 SQL 查询</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ===== 事务 =====</span></span><br><span class="line"><span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();  <span class="comment">// 开启事务</span></span><br><span class="line">tx.commit();    <span class="comment">// 提交事务</span></span><br><span class="line">tx.rollback(); <span class="comment">// 回滚事务</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ===== 其他 =====</span></span><br><span class="line">session.flush();      <span class="comment">// 立即刷新到数据库（同步缓存）</span></span><br><span class="line">session.clear();      <span class="comment">// 清空一级缓存</span></span><br><span class="line">session.evict(obj);  <span class="comment">// 从缓存中移除指定对象</span></span><br><span class="line">session.contains(obj);<span class="comment">// 判断对象是否在缓存中</span></span><br><span class="line">session.close();      <span class="comment">// 关闭 Session</span></span><br></pre></td></tr></table></figure><h3 id="4-3-get-vs-load-区别"><a href="#4-3-get-vs-load-区别" class="headerlink" title="4.3 get vs load 区别"></a>4.3 get vs load 区别</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// get 方法：立即加载，查不到返回 null</span></span><br><span class="line"><span class="type">User</span> <span class="variable">user1</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">System.out.println(user1);  <span class="comment">// 如果不存在，返回 null</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// load 方法：延迟加载（代理对象），查不到抛出异常</span></span><br><span class="line"><span class="type">User</span> <span class="variable">user2</span> <span class="operator">=</span> session.load(User.class, <span class="number">1L</span>);</span><br><span class="line">System.out.println(user2);  <span class="comment">// 如果不存在，抛出 ObjectNotFoundException</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 实际执行时机：</span></span><br><span class="line"><span class="comment">// - get()：调用时立即执行 SELECT 语句</span></span><br><span class="line"><span class="comment">// - load()：第一次访问属性时才执行 SELECT 语句（懒加载）</span></span><br></pre></td></tr></table></figure><table><thead><tr><th>特性</th><th>get()</th><th>load()</th></tr></thead><tbody><tr><td><strong>加载方式</strong></td><td>立即加载</td><td>延迟加载（返回代理对象）</td></tr><tr><td><strong>查不到时</strong></td><td>返回 null</td><td>抛出异常</td></tr><tr><td><strong>性能</strong></td><td>每次都执行 SQL</td><td>性能更好（按需加载）</td></tr><tr><td><strong>适用场景</strong></td><td>确定对象存在</td><td>不确定对象是否存在时</td></tr></tbody></table><hr><h2 id="五、CRUD-增删改查操作"><a href="#五、CRUD-增删改查操作" class="headerlink" title="五、CRUD 增删改查操作"></a>五、CRUD 增删改查操作</h2><h3 id="5-1-插入数据（INSERT）"><a href="#5-1-插入数据（INSERT）" class="headerlink" title="5.1 插入数据（INSERT）"></a>5.1 插入数据（INSERT）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HibernateCRUD</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">SessionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> HibernateUtil.getSessionFactory();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 保存用户（save）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">saveUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">            <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="type">Long</span> <span class="variable">id</span> <span class="operator">=</span> (Long) session.save(user);  <span class="comment">// save() 返回主键</span></span><br><span class="line">            </span><br><span class="line">            tx.commit();</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 用户保存成功，ID：&quot;</span> + id);</span><br><span class="line">            <span class="keyword">return</span> id;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 保存用户（persist，无返回值）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">persistUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">            <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">            </span><br><span class="line">            session.persist(user);  <span class="comment">// persist() 无返回值</span></span><br><span class="line">            </span><br><span class="line">            tx.commit();</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 用户持久化成功&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 批量插入（性能优化）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">batchInsert</span><span class="params">(List&lt;User&gt; users)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">            <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; users.size(); i++) &#123;</span><br><span class="line">                session.persist(users.get(i));</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 每 20 条刷新一次，释放内存</span></span><br><span class="line">                <span class="keyword">if</span> (i % <span class="number">20</span> == <span class="number">0</span>) &#123;</span><br><span class="line">                    session.flush();</span><br><span class="line">                    session.clear();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            tx.commit();</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 批量插入完成，共 &quot;</span> + users.size() + <span class="string">&quot; 条&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-2-查询数据（SELECT）"><a href="#5-2-查询数据（SELECT）" class="headerlink" title="5.2 查询数据（SELECT）"></a>5.2 查询数据（SELECT）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据 ID 查询用户（get）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">findById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="comment">// get() 立即加载</span></span><br><span class="line">        <span class="keyword">return</span> session.get(User.class, id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据 ID 查询用户（load，延迟加载）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">findByIdLazy</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="comment">// load() 返回代理对象，只有访问属性时才真正查询</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.load(User.class, id);</span><br><span class="line">        System.out.println(<span class="string">&quot;代理对象已创建：&quot;</span> + user.getClass().getName());</span><br><span class="line">        <span class="comment">// 只有访问属性时才执行 SELECT</span></span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询所有用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findAll</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">        <span class="keyword">return</span> query.list();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 条件查询</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findByStatus</span><span class="params">(Integer status)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE status = :status ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">        query.setParameter(<span class="string">&quot;status&quot;</span>, status);</span><br><span class="line">        <span class="keyword">return</span> query.list();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分页查询</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findByPage</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> size)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">        query.setFirstResult((page - <span class="number">1</span>) * size);  <span class="comment">// 起始位置</span></span><br><span class="line">        query.setMaxResults(size);                  <span class="comment">// 每页条数</span></span><br><span class="line">        <span class="keyword">return</span> query.list();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-3-更新数据（UPDATE）"><a href="#5-3-更新数据（UPDATE）" class="headerlink" title="5.3 更新数据（UPDATE）"></a>5.3 更新数据（UPDATE）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        session.update(user);</span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 用户更新成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新用户（merge，合并）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">mergeUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// merge：合并到持久化上下文，如果缓存中已有则合并，无则从数据库查询</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">merged</span> <span class="operator">=</span> session.merge(user);</span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 用户合并成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 动态更新（只更新非空字段）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dynamicUpdate</span><span class="params">(Long id, String newEmail)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 先查询</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, id);</span><br><span class="line">        <span class="keyword">if</span> (user != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 只更新传入的字段</span></span><br><span class="line">            <span class="keyword">if</span> (newEmail != <span class="literal">null</span> &amp;&amp; !newEmail.isEmpty()) &#123;</span><br><span class="line">                user.setEmail(newEmail);</span><br><span class="line">            &#125;</span><br><span class="line">            session.update(user);  <span class="comment">// 只更新非 null 字段对应的列</span></span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HQL 批量更新</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">batchUpdateStatus</span><span class="params">(List&lt;Long&gt; ids, Integer newStatus)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        Query&lt;Integer&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;UPDATE User SET status = :status WHERE id IN :ids&quot;</span>, Integer.class);</span><br><span class="line">        query.setParameter(<span class="string">&quot;status&quot;</span>, newStatus);</span><br><span class="line">        query.setParameterList(<span class="string">&quot;ids&quot;</span>, ids);</span><br><span class="line">        </span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> query.executeUpdate();</span><br><span class="line">        tx.commit();</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 批量更新了 &quot;</span> + rows + <span class="string">&quot; 行&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> rows;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-4-删除数据（DELETE）"><a href="#5-4-删除数据（DELETE）" class="headerlink" title="5.4 删除数据（DELETE）"></a>5.4 删除数据（DELETE）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 删除用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUser</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 先查询，再删除</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, id);</span><br><span class="line">        <span class="keyword">if</span> (user != <span class="literal">null</span>) &#123;</span><br><span class="line">            session.delete(user);</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 用户删除成功&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 删除用户（直接删除，不需要先查询）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUserDirect</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建代理对象（只包含 ID），设置主键后直接删除</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">        user.setId(id);</span><br><span class="line">        session.delete(user);</span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 用户删除成功（直接删除）&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HQL 批量删除</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">batchDelete</span><span class="params">(List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        Query&lt;Integer&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;DELETE FROM User WHERE id IN :ids&quot;</span>, Integer.class);</span><br><span class="line">        query.setParameterList(<span class="string">&quot;ids&quot;</span>, ids);</span><br><span class="line">        </span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> query.executeUpdate();</span><br><span class="line">        tx.commit();</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 批量删除了 &quot;</span> + rows + <span class="string">&quot; 行&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> rows;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="六、对象状态与一级缓存"><a href="#六、对象状态与一级缓存" class="headerlink" title="六、对象状态与一级缓存"></a>六、对象状态与一级缓存</h2><h3 id="6-1-Hibernate-对象三种状态"><a href="#6-1-Hibernate-对象三种状态" class="headerlink" title="6.1 Hibernate 对象三种状态"></a>6.1 Hibernate 对象三种状态</h3><p>理解 Hibernate 对象状态是掌握 Hibernate 的关键：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🧩 Hibernate 对象状态"] --> B["瞬时状态\nTransient"]    A --> C["持久状态\nPersistent"]    A --> D["游离状态\nDetached"]        B --> B1["new 创建\n无 ID，无 Session 关联"]    B --> B2["save() / persist()\n➡️ 持久状态"]        C --> C1["有 ID\n有 Session 关联\n在一级缓存中"]    C --> C2["get() / load() / query\n➡️ 持久状态"]    C --> C3["commit()\n➡️ 游离状态"]    C --> C4["evict() / close()\n➡️ 游离状态"]        D --> D1["有 ID\n无 Session 关联\n不在缓存中"]    D --> D2["update() / saveOrUpdate()\n➡️ 持久状态"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#c8e6c9    style D fill:#fff3e0</pre></div><h3 id="6-2-状态转换详解"><a href="#6-2-状态转换详解" class="headerlink" title="6.2 状态转换详解"></a>6.2 状态转换详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 瞬时状态（Transient）</span></span><br><span class="line"><span class="comment"> * 对象通过 new 创建，与数据库无关联，不在 Session 缓存中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">user.setUsername(<span class="string">&quot;test&quot;</span>);</span><br><span class="line">user.setPassword(<span class="string">&quot;pass&quot;</span>);  <span class="comment">// user 是瞬时状态</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 持久状态（Persistent）</span></span><br><span class="line"><span class="comment"> * 对象被保存（save）后，与数据库记录关联，在 Session 缓存中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">session.save(user);  <span class="comment">// user 变为持久状态</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 特点：</span></span><br><span class="line"><span class="comment">// 1. 对象在 Session 一级缓存中</span></span><br><span class="line"><span class="comment">// 2. 对对象的修改会自动同步到数据库（脏检查）</span></span><br><span class="line"><span class="comment">// 3. 提交事务时会自动 flush 到数据库</span></span><br><span class="line">user.setEmail(<span class="string">&quot;new@example.com&quot;</span>);  <span class="comment">// 脏检查：自动检测到修改</span></span><br><span class="line"><span class="comment">// session.flush() 或 transaction.commit() 时会执行 UPDATE</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 游离状态（Detached）</span></span><br><span class="line"><span class="comment"> * 对象曾经被持久化，但已脱离 Session 管理</span></span><br><span class="line"><span class="comment"> * 有 ID，但不在 Session 缓存中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">session.close();  <span class="comment">// Session 关闭后，之前持久化的对象变为游离状态</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 游离状态对象的复用</span></span><br><span class="line"><span class="type">User</span> <span class="variable">detachedUser</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">detachedUser.setId(<span class="number">1L</span>);</span><br><span class="line">detachedUser.setUsername(<span class="string">&quot;updated&quot;</span>);</span><br><span class="line">session.update(detachedUser);  <span class="comment">// 重新关联 Session，进入持久状态</span></span><br></pre></td></tr></table></figure><h3 id="6-3-一级缓存机制"><a href="#6-3-一级缓存机制" class="headerlink" title="6.3 一级缓存机制"></a>6.3 一级缓存机制</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 一级缓存（Session 级别）</span></span><br><span class="line"><span class="comment"> * 缓存当前 Session 中所有持久化对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cacheDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;===== 一级缓存演示 =====&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第一次查询：从数据库查询，并放入缓存</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">u1</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;第一次查询：&quot;</span> + u1.getUsername());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第二次查询：直接从缓存获取（不执行 SQL）</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">u2</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;第二次查询：&quot;</span> + u2.getUsername());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 两次查询返回的是同一个对象（内存地址相同）</span></span><br><span class="line">        System.out.println(<span class="string">&quot;是否是同一对象：&quot;</span> + (u1 == u2));  <span class="comment">// true</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 强制刷新：清除缓存，从数据库重新查询</span></span><br><span class="line">        session.evict(u1);  <span class="comment">// 从缓存移除</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">u3</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;evict 后查询：&quot;</span> + (u1 == u3));  <span class="comment">// false</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 清空所有缓存</span></span><br><span class="line">        session.clear();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 脏检查（Dirty Checking）</span></span><br><span class="line"><span class="comment"> * Hibernate 自动检测持久化对象的修改，并自动同步到数据库</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dirtyCheckingDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 修改用户属性</span></span><br><span class="line">        user.setEmail(<span class="string">&quot;dirty@example.com&quot;</span>);</span><br><span class="line">        user.setStatus(<span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 此时并未调用 update()！</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 提交事务时会：</span></span><br><span class="line">        <span class="comment">// 1. 自动执行 dirty checking（检查所有持久化对象的修改）</span></span><br><span class="line">        <span class="comment">// 2. 自动生成 UPDATE 语句</span></span><br><span class="line">        tx.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 脏检查自动更新了用户&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-4-flush-机制"><a href="#6-4-flush-机制" class="headerlink" title="6.4 flush 机制"></a>6.4 flush 机制</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * flush 模式</span></span><br><span class="line"><span class="comment"> * 控制缓存与数据库同步的时机</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">flushDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置 flush 模式</span></span><br><span class="line">        <span class="comment">// FlushMode.AUTO（默认）：在查询前和提交事务前自动同步</span></span><br><span class="line">        <span class="comment">// FlushMode.COMMIT：只在提交事务时同步</span></span><br><span class="line">        <span class="comment">// FlushMode.ALWAYS：每次查询前都同步</span></span><br><span class="line">        session.setFlushMode(FlushMode.AUTO);</span><br><span class="line">        </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 修改后，在新查询前会自动 flush</span></span><br><span class="line">        user.setStatus(<span class="number">0</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 此处如果不先查询，可以手动 flush</span></span><br><span class="line">        session.flush();  <span class="comment">// 立即同步到数据库</span></span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、映射关系配置"><a href="#七、映射关系配置" class="headerlink" title="七、映射关系配置"></a>七、映射关系配置</h2><h3 id="7-1-一对一关联（-OneToOne）"><a href="#7-1-一对一关联（-OneToOne）" class="headerlink" title="7.1 一对一关联（@OneToOne）"></a>7.1 一对一关联（@OneToOne）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户档案表：一对一关联用户表</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;user_profiles&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserProfile</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;real_name&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String realName;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;id_card&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String idCard;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Column(name = &quot;address&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String address;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@OneToOne(mappedBy = &quot;profile&quot;)</span>  <span class="comment">// 由 User 端维护外键</span></span><br><span class="line">    <span class="keyword">private</span> User user;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户表：一对一关联档案</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@OneToOne</span></span><br><span class="line">    <span class="meta">@JoinColumn(name = &quot;profile_id&quot;)</span>  <span class="comment">// 外键列（users 表）</span></span><br><span class="line">    <span class="keyword">private</span> UserProfile profile;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- XML 方式：一对一映射 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">one-to-one</span> <span class="attr">name</span>=<span class="string">&quot;profile&quot;</span> <span class="attr">class</span>=<span class="string">&quot;UserProfile&quot;</span> <span class="attr">cascade</span>=<span class="string">&quot;all&quot;</span>/&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- mappedBy 表示由对方维护外键 --&gt;</span></span><br></pre></td></tr></table></figure><h3 id="7-2-一对多关联（-OneToMany）"><a href="#7-2-一对多关联（-OneToMany）" class="headerlink" title="7.2 一对多关联（@OneToMany）"></a>7.2 一对多关联（@OneToMany）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户：一对多文章</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 一对多：一方持有多方的集合</span></span><br><span class="line">    <span class="comment">// mappedBy：由 Article 端维护外键</span></span><br><span class="line">    <span class="meta">@OneToMany(mappedBy = &quot;user&quot;, cascade = CascadeType.ALL, fetch = FetchType.LAZY)</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;Article&gt; articles = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 文章：多对一作者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;articles&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Article</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@ManyToOne(fetch = FetchType.LAZY)</span></span><br><span class="line">    <span class="meta">@JoinColumn(name = &quot;user_id&quot;)</span>  <span class="comment">// 外键列</span></span><br><span class="line">    <span class="keyword">private</span> User user;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["👤 User"] -->|"一对多\n(1:N)"| B["📝 Article"]    B -->|"多对一\n(N:1)"| A        A -->|"articles"| A1["List<Article>"]    B -->|"user"| B1["User 对象"]        style A fill:#c8e6c9    style B fill:#e3f2fd</pre></div><h3 id="7-3-多对多关联（-ManyToMany）"><a href="#7-3-多对多关联（-ManyToMany）" class="headerlink" title="7.3 多对多关联（@ManyToMany）"></a>7.3 多对多关联（@ManyToMany）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 教师：多对多学生</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;teachers&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Teacher</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 多对多：中间表 teacher_student</span></span><br><span class="line">    <span class="meta">@ManyToMany</span></span><br><span class="line">    <span class="meta">@JoinTable(</span></span><br><span class="line"><span class="meta">        name = &quot;teacher_student&quot;,</span></span><br><span class="line"><span class="meta">        joinColumns = @JoinColumn(name = &quot;teacher_id&quot;),</span></span><br><span class="line"><span class="meta">        inverseJoinColumns = @JoinColumn(name = &quot;student_id&quot;)</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">private</span> Set&lt;Student&gt; students = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 学生：多对多教师</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;students&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@ManyToMany(mappedBy = &quot;students&quot;)</span>  <span class="comment">// 由 Teacher 端维护</span></span><br><span class="line">    <span class="keyword">private</span> Set&lt;Teacher&gt; teachers = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-4-级联操作（cascade）"><a href="#7-4-级联操作（cascade）" class="headerlink" title="7.4 级联操作（cascade）"></a>7.4 级联操作（cascade）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 级联操作：操作一个对象时，自动操作关联对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// cascade 配置：级联类型</span></span><br><span class="line">    <span class="comment">// - CascadeType.ALL：所有操作都级联</span></span><br><span class="line">    <span class="comment">// - CascadeType.PERSIST：级联保存</span></span><br><span class="line">    <span class="comment">// - CascadeType.MERGE：级联合并</span></span><br><span class="line">    <span class="comment">// - CascadeType.REMOVE：级联删除</span></span><br><span class="line">    <span class="comment">// - CascadeType.REFRESH：级联刷新</span></span><br><span class="line">    <span class="meta">@OneToMany(mappedBy = &quot;user&quot;, cascade = CascadeType.ALL, orphanRemoval = true)</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;Article&gt; articles = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 级联保存示例：保存用户时自动保存文章</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cascadeSaveDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">        user.setUsername(<span class="string">&quot;author&quot;</span>);</span><br><span class="line">        user.setEmail(<span class="string">&quot;author@example.com&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">Article</span> <span class="variable">article</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Article</span>();</span><br><span class="line">        article.setTitle(<span class="string">&quot;Hibernate 级联操作&quot;</span>);</span><br><span class="line">        article.setContent(<span class="string">&quot;本文讲解级联操作...&quot;</span>);</span><br><span class="line">        article.setUser(user);  <span class="comment">// 关联</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 级联保存：只保存 user，自动保存 article</span></span><br><span class="line">        session.save(user);</span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 级联保存成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 级联删除示例：删除用户时自动删除文章</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cascadeDeleteDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> session.beginTransaction();</span><br><span class="line">        </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        session.delete(user);  <span class="comment">// 级联删除，自动删除关联的文章</span></span><br><span class="line">        </span><br><span class="line">        tx.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 级联删除成功（用户和文章都删了）&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、HQL-查询语言"><a href="#八、HQL-查询语言" class="headerlink" title="八、HQL 查询语言"></a>八、HQL 查询语言</h2><h3 id="8-1-HQL-基础语法"><a href="#8-1-HQL-基础语法" class="headerlink" title="8.1 HQL 基础语法"></a>8.1 HQL 基础语法</h3><p>HQL（Hibernate Query Language）是面向对象的查询语言，操作的是对象和属性：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📝 SQL vs HQL"] --> B["SQL：面向表和列"]    A --> C["HQL：面向对象和属性"]        B --> B1["SELECT * FROM users"]    B1 --> B2["SELECT u.* FROM users u"]    B2 --> B3["SELECT * FROM users WHERE id = 1"]        C --> C1["FROM User"]    C1 --> C2["SELECT u FROM User u"]    C2 --> C3["FROM User WHERE id = 1"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#c8e6c9</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HQL 查询基础</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hqlDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="comment">// 1. 查询所有</span></span><br><span class="line">        Query&lt;User&gt; q1 = session.createQuery(<span class="string">&quot;FROM User&quot;</span>, User.class);</span><br><span class="line">        List&lt;User&gt; users = q1.list();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 条件查询（命名参数）</span></span><br><span class="line">        Query&lt;User&gt; q2 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE status = :status ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">        q2.setParameter(<span class="string">&quot;status&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        List&lt;User&gt; activeUsers = q2.list();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. 条件查询（位置参数）</span></span><br><span class="line">        Query&lt;User&gt; q3 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE status = ?1 ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">        q3.setParameter(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 4. 分页查询</span></span><br><span class="line">        Query&lt;User&gt; q4 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">        q4.setFirstResult(<span class="number">0</span>);   <span class="comment">// 起始位置</span></span><br><span class="line">        q4.setMaxResults(<span class="number">10</span>);    <span class="comment">// 每页条数</span></span><br><span class="line">        List&lt;User&gt; page1 = q4.list();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 5. 单个查询（uniqueResult）</span></span><br><span class="line">        Query&lt;User&gt; q5 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE id = :id&quot;</span>, User.class);</span><br><span class="line">        q5.setParameter(<span class="string">&quot;id&quot;</span>, <span class="number">1L</span>);</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> q5.uniqueResult();  <span class="comment">// 确保只有一条记录</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 6. 聚合查询</span></span><br><span class="line">        Query&lt;Long&gt; q6 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT COUNT(*) FROM User&quot;</span>, Long.class);</span><br><span class="line">        <span class="type">Long</span> <span class="variable">count</span> <span class="operator">=</span> q6.uniqueResult();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 7. 投影查询（只查询部分字段，返回 Object[] 或 DTO）</span></span><br><span class="line">        Query&lt;Object[]&gt; q7 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT u.id, u.username, u.email FROM User u&quot;</span>, Object[].class);</span><br><span class="line">        List&lt;Object[]&gt; results = q7.list();</span><br><span class="line">        <span class="keyword">for</span> (Object[] row : results) &#123;</span><br><span class="line">            System.out.println(row[<span class="number">0</span>] + <span class="string">&quot;-&quot;</span> + row[<span class="number">1</span>] + <span class="string">&quot;-&quot;</span> + row[<span class="number">2</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-2-HQL-高级特性"><a href="#8-2-HQL-高级特性" class="headerlink" title="8.2 HQL 高级特性"></a>8.2 HQL 高级特性</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * HQL 函数与表达式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hqlAdvancedDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 1. 模糊查询</span></span><br><span class="line">        Query&lt;User&gt; q1 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE username LIKE :kw&quot;</span>, User.class);</span><br><span class="line">        q1.setParameter(<span class="string">&quot;kw&quot;</span>, <span class="string">&quot;%test%&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. IN 查询</span></span><br><span class="line">        Query&lt;User&gt; q2 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE status IN (:statuses)&quot;</span>, User.class);</span><br><span class="line">        q2.setParameterList(<span class="string">&quot;statuses&quot;</span>, Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>));</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. BETWEEN 查询</span></span><br><span class="line">        Query&lt;User&gt; q3 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE id BETWEEN :min AND :max&quot;</span>, User.class);</span><br><span class="line">        q3.setParameter(<span class="string">&quot;min&quot;</span>, <span class="number">1L</span>);</span><br><span class="line">        q3.setParameter(<span class="string">&quot;max&quot;</span>, <span class="number">10L</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 4. IS NULL / IS NOT NULL</span></span><br><span class="line">        Query&lt;User&gt; q4 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE email IS NULL OR email = &#x27;&#x27;&quot;</span>, User.class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 5. 排序</span></span><br><span class="line">        Query&lt;User&gt; q5 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User ORDER BY createdAt DESC, username ASC&quot;</span>, User.class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 6. 分组 + 聚合</span></span><br><span class="line">        Query&lt;Object[]&gt; q6 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT u.status, COUNT(u), MAX(u.createdAt) &quot;</span> +</span><br><span class="line">            <span class="string">&quot;FROM User u GROUP BY u.status&quot;</span>, Object[].class);</span><br><span class="line">        List&lt;Object[]&gt; stats = q6.list();</span><br><span class="line">        <span class="keyword">for</span> (Object[] row : stats) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;状态 &quot;</span> + row[<span class="number">0</span>] + <span class="string">&quot;：共 &quot;</span> + row[<span class="number">1</span>] + <span class="string">&quot; 人，最新 &quot;</span> + row[<span class="number">2</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 7. HAVING 子句</span></span><br><span class="line">        Query&lt;Object[]&gt; q7 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT u.status, COUNT(u) &quot;</span> +</span><br><span class="line">            <span class="string">&quot;FROM User u GROUP BY u.status HAVING COUNT(u) &gt; 5&quot;</span>, Object[].class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 8. 关联查询（JOIN FETCH 预加载）</span></span><br><span class="line">        Query&lt;User&gt; q8 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT DISTINCT u FROM User u &quot;</span> +</span><br><span class="line">            <span class="string">&quot;LEFT JOIN FETCH u.articles &quot;</span> +</span><br><span class="line">            <span class="string">&quot;WHERE u.id = :id&quot;</span>, User.class);</span><br><span class="line">        q8.setParameter(<span class="string">&quot;id&quot;</span>, <span class="number">1L</span>);</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> q8.uniqueResult();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 9. 子查询</span></span><br><span class="line">        Query&lt;User&gt; q9 = session.createQuery(</span><br><span class="line">            <span class="string">&quot;FROM User WHERE status = (SELECT MAX(u.status) FROM User u)&quot;</span>, User.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-原生-SQL-查询"><a href="#8-3-原生-SQL-查询" class="headerlink" title="8.3 原生 SQL 查询"></a>8.3 原生 SQL 查询</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 原生 SQL 查询</span></span><br><span class="line"><span class="comment"> * 当 HQL 无法满足需求时使用</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">nativeSqlDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 1. 原生 SQL 查询（返回实体）</span></span><br><span class="line">        SQLQuery&lt;User&gt; q1 = session.createSQLQuery(</span><br><span class="line">            <span class="string">&quot;SELECT * FROM users WHERE status = :status&quot;</span>, User.class);</span><br><span class="line">        q1.setParameter(<span class="string">&quot;status&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        List&lt;User&gt; users = q1.list();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 原生 SQL 返回部分字段（需要添加实体映射）</span></span><br><span class="line">        SQLQuery&lt;Object[]&gt; q2 = session.createSQLQuery(</span><br><span class="line">            <span class="string">&quot;SELECT id, username, email FROM users&quot;</span>, Object[].class);</span><br><span class="line">        List&lt;Object[]&gt; partialUsers = q2.list();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. 调用存储过程</span></span><br><span class="line">        <span class="type">SQLQuery</span> <span class="variable">q3</span> <span class="operator">=</span> session.createSQLQuery(<span class="string">&quot;CALL get_user_stats()&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 4. 添加结果集映射</span></span><br><span class="line">        <span class="type">SQLQuery</span> <span class="variable">q4</span> <span class="operator">=</span> session.createSQLQuery(</span><br><span class="line">            <span class="string">&quot;SELECT u.id, u.username, COUNT(a.id) as article_count &quot;</span> +</span><br><span class="line">            <span class="string">&quot;FROM users u LEFT JOIN articles a ON u.id = a.user_id &quot;</span> +</span><br><span class="line">            <span class="string">&quot;GROUP BY u.id, u.username&quot;</span>);</span><br><span class="line">        q4.addScalar(<span class="string">&quot;id&quot;</span>, LongType.INSTANCE);</span><br><span class="line">        q4.addScalar(<span class="string">&quot;username&quot;</span>, StringType.INSTANCE);</span><br><span class="line">        q4.addScalar(<span class="string">&quot;article_count&quot;</span>, LongType.INSTANCE);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、事务管理"><a href="#九、事务管理" class="headerlink" title="九、事务管理"></a>九、事务管理</h2><h3 id="9-1-Hibernate-事务概念"><a href="#9-1-Hibernate-事务概念" class="headerlink" title="9.1 Hibernate 事务概念"></a>9.1 Hibernate 事务概念</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔒 事务特性 ACID"] --> A1["A - Atomicity\n原子性"]    A --> A2["C - Consistency\n一致性"]    A --> A3["I - Isolation\n隔离性"]    A --> A4["D - Durability\n持久性"]        A1 --> A1a["全部成功\n或全部失败"]    A2 --> A2a["事务前后\n数据合法一致"]    A3 --> A3a["并发事务\n互不干扰"]    A4 --> A4a["提交后\n永久保存"]        style A fill:#fff3e0    style A1 fill:#c8e6c9    style A2 fill:#c8e6c9    style A3 fill:#fff3e0    style A4 fill:#f8bbd0</pre></div><h3 id="9-2-声明式事务（Spring-环境）"><a href="#9-2-声明式事务（Spring-环境）" class="headerlink" title="9.2 声明式事务（Spring 环境）"></a>9.2 声明式事务（Spring 环境）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring + Hibernate 事务管理（推荐）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Transactional</span>  <span class="comment">// 声明式事务</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Long fromId, Long toId, BigDecimal amount)</span> &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">from</span> <span class="operator">=</span> userRepository.findById(fromId);</span><br><span class="line">        <span class="type">User</span> <span class="variable">to</span> <span class="operator">=</span> userRepository.findById(toId);</span><br><span class="line">        </span><br><span class="line">        from.setBalance(from.getBalance().subtract(amount));</span><br><span class="line">        to.setBalance(to.getBalance().add(amount));</span><br><span class="line">        </span><br><span class="line">        userRepository.save(from);</span><br><span class="line">        userRepository.save(to);</span><br><span class="line">        <span class="comment">// 事务会自动提交</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-3-编程式事务"><a href="#9-3-编程式事务" class="headerlink" title="9.3 编程式事务"></a>9.3 编程式事务</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 原生 Hibernate 事务管理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transactionDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 开启事务</span></span><br><span class="line">            tx = session.beginTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 操作 1：保存用户</span></span><br><span class="line">            <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">            user.setUsername(<span class="string">&quot;new_user&quot;</span>);</span><br><span class="line">            user.setPassword(<span class="string">&quot;pass&quot;</span>);</span><br><span class="line">            session.save(user);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 操作 2：保存文章</span></span><br><span class="line">            <span class="type">Article</span> <span class="variable">article</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Article</span>();</span><br><span class="line">            article.setTitle(<span class="string">&quot;New Article&quot;</span>);</span><br><span class="line">            article.setUser(user);</span><br><span class="line">            session.save(article);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 操作 3：故意出错（余额不足）</span></span><br><span class="line">            <span class="keyword">if</span> (user.getBalance().compareTo(amount) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;余额不足&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            tx.commit();</span><br><span class="line">            System.out.println(<span class="string">&quot;✅ 事务提交成功&quot;</span>);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">// 发生异常，回滚事务</span></span><br><span class="line">            <span class="keyword">if</span> (tx != <span class="literal">null</span>) &#123;</span><br><span class="line">                tx.rollback();</span><br><span class="line">                System.out.println(<span class="string">&quot;🔄 事务已回滚&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-4-事务隔离级别"><a href="#9-4-事务隔离级别" class="headerlink" title="9.4 事务隔离级别"></a>9.4 事务隔离级别</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- hibernate.cfg.xml 配置 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.connection.isolation&quot;</span>&gt;</span>2<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">    隔离级别：</span></span><br><span class="line"><span class="comment">    1 - READ UNCOMMITTED</span></span><br><span class="line"><span class="comment">    2 - READ COMMITTED（MySQL 默认）</span></span><br><span class="line"><span class="comment">    4 - REPEATABLE READ（MySQL 默认）</span></span><br><span class="line"><span class="comment">    8 - SERIALIZABLE</span></span><br><span class="line"><span class="comment">--&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="十、延迟加载与抓取策略"><a href="#十、延迟加载与抓取策略" class="headerlink" title="十、延迟加载与抓取策略"></a>十、延迟加载与抓取策略</h2><h3 id="10-1-延迟加载（Lazy-Loading）"><a href="#10-1-延迟加载（Lazy-Loading）" class="headerlink" title="10.1 延迟加载（Lazy Loading）"></a>10.1 延迟加载（Lazy Loading）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 延迟加载：只在访问关联对象时才查询</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lazyLoadingDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 查询用户（不立即查询关联的文章）</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;用户：&quot;</span> + user.getUsername());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 只有访问 articles 时才查询（LazyInitializationException：如果 Session 已关闭）</span></span><br><span class="line">        <span class="comment">// user.getArticles() 返回的是代理对象，只有真正访问时才触发查询</span></span><br><span class="line">        List&lt;Article&gt; articles = user.getArticles();  <span class="comment">// 触发 SELECT</span></span><br><span class="line">        System.out.println(<span class="string">&quot;文章数：&quot;</span> + articles.size());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 如果 Session 已关闭，访问延迟加载的属性会抛出 LazyInitializationException</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 解决懒加载异常：使用 Hibernate.initialize()</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lazyLoadingSolutionDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> session.get(User.class, <span class="number">1L</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 在 Session 关闭前初始化关联对象</span></span><br><span class="line">        Hibernate.initialize(user.getArticles());  <span class="comment">// 立即加载</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 或者使用 fetch join 查询时预加载</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="10-2-抓取策略（Fetch-Strategy）"><a href="#10-2-抓取策略（Fetch-Strategy）" class="headerlink" title="10.2 抓取策略（Fetch Strategy）"></a>10.2 抓取策略（Fetch Strategy）</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["⚡ 抓取策略"] --> B["立即加载\nEager"]    A --> C["延迟加载\nLazy"]    A --> D["批量抓取\nBatch"]    A --> E["子查询抓取\nSubselect"]        B --> B1["查询时立即\n加载关联对象"]    C --> C1["访问属性时\n才加载关联对象"]    D --> D1["批量加载\n多个代理对象"]    E --> E1["子查询\n一次加载"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#e3f2fd</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 抓取策略配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 一对多：默认延迟加载，可配置抓取策略</span></span><br><span class="line">    <span class="meta">@OneToMany(mappedBy = &quot;user&quot;, fetch = FetchType.LAZY)</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;Article&gt; articles;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 批量抓取大小</span></span><br><span class="line">    <span class="meta">@BatchSize(size = 20)</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;Article&gt; articles2;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * JOIN FETCH 预加载（推荐）</span></span><br><span class="line"><span class="comment"> * 在 HQL 中使用 JOIN FETCH 一次性加载所有数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">joinFetchDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT DISTINCT u FROM User u &quot;</span> +</span><br><span class="line">            <span class="string">&quot;LEFT JOIN FETCH u.articles &quot;</span> +</span><br><span class="line">            <span class="string">&quot;WHERE u.id = :id&quot;</span>, User.class);</span><br><span class="line">        query.setParameter(<span class="string">&quot;id&quot;</span>, <span class="number">1L</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> query.uniqueResult();</span><br><span class="line">        <span class="comment">// 一次查询就加载了 user 和其所有 articles</span></span><br><span class="line">        <span class="comment">// 不需要再次查询</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="10-3-N-1-问题与解决"><a href="#10-3-N-1-问题与解决" class="headerlink" title="10.3 N+1 问题与解决"></a>10.3 N+1 问题与解决</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * N+1 问题示例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">nPlusOneDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="comment">// N+1 问题：1 次查询用户 + N 次查询每个用户的文章</span></span><br><span class="line">        List&lt;User&gt; users = session.createQuery(<span class="string">&quot;FROM User&quot;</span>, User.class).list();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (User user : users) &#123;</span><br><span class="line">            <span class="comment">// 每个用户都会触发一次查询文章</span></span><br><span class="line">            System.out.println(user.getUsername() + <span class="string">&quot; - &quot;</span> + user.getArticles().size());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 总共执行 1 + N 次 SQL</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 解决 N+1 问题：使用 JOIN FETCH</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">solveNPlusOneDemo</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">        <span class="comment">// 解决方案：JOIN FETCH 一次性加载</span></span><br><span class="line">        Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">            <span class="string">&quot;SELECT DISTINCT u FROM User u &quot;</span> +</span><br><span class="line">            <span class="string">&quot;LEFT JOIN FETCH u.articles&quot;</span>, User.class);</span><br><span class="line">        List&lt;User&gt; users = query.list();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (User user : users) &#123;</span><br><span class="line">            <span class="comment">// 不再触发额外查询，直接使用已加载的数据</span></span><br><span class="line">            System.out.println(user.getUsername() + <span class="string">&quot; - &quot;</span> + user.getArticles().size());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 只执行 1 次 SQL</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十一、与-Spring-集成"><a href="#十一、与-Spring-集成" class="headerlink" title="十一、与 Spring 集成"></a>十一、与 Spring 集成</h2><h3 id="11-1-Spring-Hibernate-整合配置"><a href="#11-1-Spring-Hibernate-整合配置" class="headerlink" title="11.1 Spring + Hibernate 整合配置"></a>11.1 Spring + Hibernate 整合配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spring 配置 Hibernate（XML 方式）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span>  <span class="comment">// 开启事务管理</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HibernateConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> LocalSessionFactoryBean <span class="title function_">sessionFactory</span><span class="params">(DataSource dataSource)</span> &#123;</span><br><span class="line">        <span class="type">LocalSessionFactoryBean</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LocalSessionFactoryBean</span>();</span><br><span class="line">        factory.setDataSource(dataSource);</span><br><span class="line">        factory.setPackagesToScan(<span class="string">&quot;com.example.entity&quot;</span>);</span><br><span class="line">        factory.setHibernateProperties(hibernateProperties());</span><br><span class="line">        <span class="keyword">return</span> factory;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Properties <span class="title function_">hibernateProperties</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Properties</span> <span class="variable">props</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">        props.setProperty(<span class="string">&quot;hibernate.dialect&quot;</span>, <span class="string">&quot;org.hibernate.dialect.MySQLDialect&quot;</span>);</span><br><span class="line">        props.setProperty(<span class="string">&quot;hibernate.show_sql&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">        props.setProperty(<span class="string">&quot;hibernate.format_sql&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">        props.setProperty(<span class="string">&quot;hibernate.hbm2ddl.auto&quot;</span>, <span class="string">&quot;update&quot;</span>);</span><br><span class="line">        props.setProperty(<span class="string">&quot;hibernate.cache.use_second_level_cache&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> props;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> HibernateTransactionManager <span class="title function_">transactionManager</span><span class="params">(SessionFactory sessionFactory)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">HibernateTransactionManager</span>(sessionFactory);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="11-2-Spring-Data-JPA-简化开发"><a href="#11-2-Spring-Data-JPA-简化开发" class="headerlink" title="11.2 Spring Data JPA 简化开发"></a>11.2 Spring Data JPA 简化开发</h3><p>Spring Data JPA 是 Spring 生态中简化 JPA&#x2F;Hibernate 开发的利器：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 继承 JpaRepository，自动拥有 CRUD 功能</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserRepository</span> <span class="keyword">extends</span> <span class="title class_">JpaRepository</span>&lt;User, Long&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 按方法名自动解析查询（无需编写实现）</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByUsername</span><span class="params">(String username)</span>;</span><br><span class="line">    </span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByStatus</span><span class="params">(Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByUsernameContainingAndStatus</span><span class="params">(String keyword, Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Query(&quot;FROM User WHERE username = :username&quot;)</span></span><br><span class="line">    User <span class="title function_">findUserByUsername</span><span class="params">(<span class="meta">@Param(&quot;username&quot;)</span> String username)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Modifying</span></span><br><span class="line">    <span class="meta">@Query(&quot;UPDATE User SET status = :status WHERE id = :id&quot;)</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">updateStatus</span><span class="params">(<span class="meta">@Param(&quot;id&quot;)</span> Long id, <span class="meta">@Param(&quot;status&quot;)</span> Integer status)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Service 层使用</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.findById(id).orElse(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">getActiveUsers</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.findByStatus(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getByUsername</span><span class="params">(String username)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userRepository.findUserByUsername(username);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十二、常见问题与最佳实践"><a href="#十二、常见问题与最佳实践" class="headerlink" title="十二、常见问题与最佳实践"></a>十二、常见问题与最佳实践</h2><h3 id="12-1-常见问题与解决方案"><a href="#12-1-常见问题与解决方案" class="headerlink" title="12.1 常见问题与解决方案"></a>12.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["LazyInitializationException"]    A --> C["N+1 查询"]    A --> D["事务未提交"]    A --> E["对象状态混乱"]        B --> B1["✅ Session 已关闭\n访问懒加载对象"]    B1 --> B2["方案：\nJOIN FETCH\n或开启 long Session"]        C --> C1["✅ 查询 N+1\n性能问题"]    C1 --> C2["方案：\n+1 配置\n或 JOIN FETCH"]        D --> D1["✅ 数据未保存\n到数据库"]    D1 --> D2["方案：\n检查事务配置\n是否 @Transactional"]        E --> E1["✅ 对象状态\n不符合预期"]    E1 --> E2["方案：\n理解三种状态\n使用 merge()"]        style A fill:#fff3e0</pre></div><h3 id="12-2-性能优化技巧"><a href="#12-2-性能优化技巧" class="headerlink" title="12.2 性能优化技巧"></a>12.2 性能优化技巧</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Hibernate 性能优化技巧</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 使用抓取策略减少 SQL 查询</span></span><br><span class="line">Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">    <span class="string">&quot;SELECT DISTINCT u FROM User u &quot;</span> +</span><br><span class="line">    <span class="string">&quot;LEFT JOIN FETCH u.articles &quot;</span> +</span><br><span class="line">    <span class="string">&quot;WHERE u.id = :id&quot;</span>, User.class);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 使用批量抓取</span></span><br><span class="line"><span class="meta">@OneToMany(mappedBy = &quot;user&quot;)</span></span><br><span class="line"><span class="meta">@BatchSize(size = 20)</span></span><br><span class="line"><span class="keyword">private</span> List&lt;Article&gt; articles;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 启用二级缓存</span></span><br><span class="line"><span class="meta">@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = &quot;users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 只查询需要的字段（投影查询）</span></span><br><span class="line">Query&lt;Object[]&gt; query = session.createQuery(</span><br><span class="line">    <span class="string">&quot;SELECT u.id, u.username FROM User u&quot;</span>, Object[].class);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 使用分页查询</span></span><br><span class="line">Query&lt;User&gt; query = session.createQuery(</span><br><span class="line">    <span class="string">&quot;FROM User ORDER BY createdAt DESC&quot;</span>, User.class);</span><br><span class="line">query.setFirstResult(<span class="number">0</span>);</span><br><span class="line">query.setMaxResults(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 及时释放资源</span></span><br><span class="line"><span class="keyword">try</span> (<span class="type">Session</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">    <span class="comment">// 操作</span></span><br><span class="line">&#125;  <span class="comment">// 自动关闭 Session</span></span><br></pre></td></tr></table></figure><h3 id="12-3-配置清单"><a href="#12-3-配置清单" class="headerlink" title="12.3 配置清单"></a>12.3 配置清单</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- hibernate.cfg.xml 推荐配置 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">session-factory</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- SQL 配置 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.show_sql&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.format_sql&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.hbm2ddl.auto&quot;</span>&gt;</span>update<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 性能配置 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.jdbc.batch_size&quot;</span>&gt;</span>20<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.order_inserts&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.order_updates&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.jdbc.batch_versioned_data&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 缓存配置 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.cache.use_second_level_cache&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernate.cache.use_query_cache&quot;</span>&gt;</span>true<span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">session-factory</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="十三、总结"><a href="#十三、总结" class="headerlink" title="十三、总结"></a>十三、总结</h2><h3 id="13-1-核心知识点回顾"><a href="#13-1-核心知识点回顾" class="headerlink" title="13.1 核心知识点回顾"></a>13.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Hibernate 核心知识))    基础入门      ORM 概念      环境搭建      配置详解    实体映射      XML 映射      JPA 注解      主键生成策略    核心 API      SessionFactory      Session      Transaction      get vs load    对象状态      瞬时状态      持久状态      游离状态      一级缓存      脏检查    映射关系      一对一      一对多      多对多      级联操作    HQL 查询      查询语法      参数绑定      聚合函数      原生 SQL    性能优化      延迟加载      JOIN FETCH      批量抓取      二级缓存    Spring 集成      Spring Data JPA      事务管理      配置优化</pre></div><h3 id="13-2-学习路线建议"><a href="#13-2-学习路线建议" class="headerlink" title="13.2 学习路线建议"></a>13.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["第一阶段\n入门"] --> B["第二阶段\n核心概念"]    B --> C["第三阶段\n映射配置"]    C --> D["第四阶段\nHQL 查询"]    D --> E["第五阶段\n高级特性"]    E --> F["第六阶段\nSpring 集成"]        A --> A1["ORM 概念"]    A --> A2["环境搭建"]        B --> B1["对象状态"]    B --> B2["Session API"]    B --> B3["事务管理"]        C --> C1["实体映射"]    C --> C2["关联映射"]    C --> C3["级联操作"]        D --> D1["HQL 语法"]    D --> D2["参数绑定"]    D --> D3["原生 SQL"]        E --> E1["缓存机制"]    E --> E2["性能优化"]    E --> E3["延迟加载"]        F --> F1["Spring Data JPA"]    F --> F2["声明式事务"]    F --> F3["配置调优"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0    style E fill:#ffcdd2    style F fill:#e3f2fd</pre></div><h3 id="13-3-下一步推荐学习"><a href="#13-3-下一步推荐学习" class="headerlink" title="13.3 下一步推荐学习"></a>13.3 下一步推荐学习</h3><ul><li>📖 <strong>Spring Data JPA</strong>：简化 JPA&#x2F;Hibernate 开发</li><li>📖 <strong>MyBatis</strong>：对比学习半自动 ORM</li><li>📖 <strong>Hibernate 源码</strong>：理解框架设计思想</li><li>📖 <strong>数据库优化</strong>：索引、SQL 调优</li><li>📖 <strong>分布式数据库</strong>：分库分表、读写分离</li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：Hibernate 虽然在国内被 MyBatis 盖过了风头，但它在 ORM 领域的经典地位和设计思想依然值得深入学习。理解 Hibernate 的对象状态、缓存机制、延迟加载等核心概念，对你理解其他 ORM 框架甚至整个 Java 持久层生态都有极大帮助。无论是 Hibernate 还是 MyBatis，选择合适的工具做好工作才是最重要的！🚀</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/hibernate-complete-guide/</id>
    <link href="https://blog.codenav.top/hibernate-complete-guide/"/>
    <published>2026-05-24T05:36:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Hibernate-框架从入门到实战：全自动化-ORM-的力量-🔮"><a href="#Hibernate-框架从入门到实战：全自动化-ORM-的力量-🔮" class="headerlink" title="Hibernate 框架从入门到实战：全自动化]]>
    </summary>
    <title>Hibernate 框架从入门到实战：全自动化 ORM 的力量 🔮</title>
    <updated>2026-05-24T05:37:46.527Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="ORM" scheme="https://blog.codenav.top/tags/ORM/"/>
    <category term="持久层" scheme="https://blog.codenav.top/tags/%E6%8C%81%E4%B9%85%E5%B1%82/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="MyBatis" scheme="https://blog.codenav.top/tags/MyBatis/"/>
    <content>
      <![CDATA[<h1 id="MyBatis-从入门到精通：手把手教你玩转持久层框架-🗂️"><a href="#MyBatis-从入门到精通：手把手教你玩转持久层框架-🗂️" class="headerlink" title="MyBatis 从入门到精通：手把手教你玩转持久层框架 🗂️"></a>MyBatis 从入门到精通：手把手教你玩转持久层框架 🗂️</h1><blockquote><p>MyBatis 是 Java 领域最受欢迎的持久层框架之一，它解决了 JDBC 操作数据库时的繁琐冗长问题，让数据库操作变得优雅而高效。本文将从零开始，系统讲解 MyBatis 的核心概念、使用方法、高级特性以及最佳实践，帮你真正掌握这门实用技术！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80mybatis-%E6%A6%82%E8%BF%B0%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-mybatis">一、MyBatis 概述：为什么选择 MyBatis？</a></li><li><a href="#%E4%BA%8C%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8">二、环境搭建与快速入门</a></li><li><a href="#%E4%B8%89mybatis-%E6%A0%B8%E5%BF%83%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3">三、MyBatis 核心组件详解</a></li><li><a href="#%E5%9B%9Bcrud-%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E6%93%8D%E4%BD%9C">四、CRUD 增删改查操作</a></li><li><a href="#%E4%BA%94%E5%8A%A8%E6%80%81-sql%E8%AE%A9-sql-%E7%81%B5%E5%8A%A8%E8%B5%B7%E6%9D%A5">五、动态 SQL：让 SQL 灵动起来</a></li><li><a href="#%E5%85%AD%E6%98%A0%E5%B0%84%E5%99%A8%E9%85%8D%E7%BD%AE%E4%B8%8E%E8%87%AA%E5%8A%A8%E6%98%A0%E5%B0%84">六、映射器配置与自动映射</a></li><li><a href="#%E4%B8%83%E7%B1%BB%E5%9E%8B%E5%A4%84%E7%90%86%E5%99%A8%E4%B8%8E%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2">七、类型处理器与类型转换</a></li><li><a href="#%E5%85%AB%E5%88%86%E9%A1%B5%E6%8F%92%E4%BB%B6%E4%B8%8E%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">八、分页插件与性能优化</a></li><li><a href="#%E4%B9%9Dmybatis-plus-%E8%BF%9B%E9%98%B6%E6%89%A9%E5%B1%95">九、MyBatis-Plus 进阶扩展</a></li><li><a href="#%E5%8D%81%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">十、常见问题与最佳实践</a></li><li><a href="#%E5%8D%81%E4%B8%80%E6%80%BB%E7%BB%93">十一、总结</a></li></ul><hr><h2 id="一、MyBatis-概述：为什么选择-MyBatis？"><a href="#一、MyBatis-概述：为什么选择-MyBatis？" class="headerlink" title="一、MyBatis 概述：为什么选择 MyBatis？"></a>一、MyBatis 概述：为什么选择 MyBatis？</h2><h3 id="1-1-JDBC-的痛点"><a href="#1-1-JDBC-的痛点" class="headerlink" title="1.1 JDBC 的痛点"></a>1.1 JDBC 的痛点</h3><p>在 MyBatis 诞生之前，Java 操作数据库主要依赖 JDBC。JDBC 虽然强大，但使用起来非常繁琐：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JDBC 方式：查询用户信息</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">findUserById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 加载驱动</span></span><br><span class="line">        Class.forName(<span class="string">&quot;com.mysql.cj.jdbc.Driver&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 2. 建立连接</span></span><br><span class="line">        conn = DriverManager.getConnection(</span><br><span class="line">            <span class="string">&quot;jdbc:mysql://localhost:3306/mybatis_demo&quot;</span>,</span><br><span class="line">            <span class="string">&quot;root&quot;</span>, <span class="string">&quot;password&quot;</span></span><br><span class="line">        );</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 3. 编写 SQL</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 4. 预编译 SQL</span></span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        ps.setLong(<span class="number">1</span>, id);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 5. 执行查询</span></span><br><span class="line">        rs = ps.executeQuery();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 6. 手动映射结果集到对象</span></span><br><span class="line">        <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">            user = <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">            user.setId(rs.getLong(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">            user.setUsername(rs.getString(<span class="string">&quot;username&quot;</span>));</span><br><span class="line">            user.setPassword(rs.getString(<span class="string">&quot;password&quot;</span>));</span><br><span class="line">            user.setEmail(rs.getString(<span class="string">&quot;email&quot;</span>));</span><br><span class="line">            user.setStatus(rs.getInt(<span class="string">&quot;status&quot;</span>));</span><br><span class="line">            user.setCreatedAt(rs.getTimestamp(<span class="string">&quot;created_at&quot;</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 7. 关闭资源（繁琐且容易出错）</span></span><br><span class="line">        <span class="keyword">if</span> (rs != <span class="literal">null</span>) <span class="keyword">try</span> &#123; rs.close(); &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">        <span class="keyword">if</span> (ps != <span class="literal">null</span>) <span class="keyword">try</span> &#123; ps.close(); &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) <span class="keyword">try</span> &#123; conn.close(); &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> user;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>JDBC 的问题</strong>：</p><ul><li>❌ 代码冗长，每个方法都要写大量模板代码</li><li>❌ SQL 语句硬编码在 Java 代码中，维护困难</li><li>❌ 参数映射和结果集映射需要手动编写，容易出错</li><li>❌ 资源管理繁琐，容易造成资源泄漏</li></ul><h3 id="1-2-MyBatis-的解决方案"><a href="#1-2-MyBatis-的解决方案" class="headerlink" title="1.2 MyBatis 的解决方案"></a>1.2 MyBatis 的解决方案</h3><p>MyBatis 通过 XML 或注解配置 SQL，并自动处理参数映射和结果集映射，大大简化了数据库操作：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["☕️ Java 代码"] --> B["📋 MyBatis"]    B --> C["🗄️ 数据库"]        A -->|1. 调用 API| D["SqlSession"]    D -->|2. 执行 Mapper| E["SQL XML / 注解"]    E -->|3. 映射| F["输入参数\n自动映射"]    E -->|4. 映射| G["结果集\n自动映射"]    F --> D    G --> D    D -->|5. 返回结果| A        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#f8bbd0</pre></div><h3 id="1-3-MyBatis-vs-Hibernate-对比"><a href="#1-3-MyBatis-vs-Hibernate-对比" class="headerlink" title="1.3 MyBatis vs Hibernate 对比"></a>1.3 MyBatis vs Hibernate 对比</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["ORM 框架对比"] --> B["Hibernate\n全自动 ORM"]    A --> C["MyBatis\n半自动 ORM"]        B --> B1["✅ 全自动\n无需写 SQL"]    B --> B2["✅ 屏蔽数据库差异\n数据库无关"]    B --> B3["❌ SQL 不可控\n性能调优困难"]    B --> B4["❌ 学习成本高\n配置复杂"]        C --> C1["✅ SQL 完全可控\n性能优异"]    C --> C2["✅ 学习曲线平缓\n上手快"]    C --> C3["✅ 灵活定制 SQL\n复杂查询轻松"]    C --> C4["❌ 需要手写 SQL\n有一定工作量"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#c8e6c9</pre></div><table><thead><tr><th>对比维度</th><th>Hibernate</th><th>MyBatis</th></tr></thead><tbody><tr><td><strong>SQL 控制</strong></td><td>框架自动生成</td><td>手动编写，完全可控</td></tr><tr><td><strong>学习曲线</strong></td><td>陡峭</td><td>平缓</td></tr><tr><td><strong>性能调优</strong></td><td>较难</td><td>容易（可直接优化 SQL）</td></tr><tr><td><strong>数据库无关性</strong></td><td>完全无关</td><td>需要适配不同数据库</td></tr><tr><td><strong>关联查询</strong></td><td>自动处理，但复杂</td><td>需要手写 JOIN</td></tr><tr><td><strong>适用场景</strong></td><td>业务简单、以查询为主</td><td>复杂业务、需要性能优化</td></tr><tr><td><strong>市场占有率</strong></td><td>逐渐下降</td><td>持续上升（国内主流）</td></tr></tbody></table><blockquote><p>💡 <strong>国内现状</strong>：MyBatis 凭借其灵活性和可控性，在国内 Java 企业中占据了绝对主流地位。阿里开源的 <strong>MyBatis-Plus</strong> 更是进一步简化了开发，几乎成为必学技能。</p></blockquote><hr><h2 id="二、环境搭建与快速入门"><a href="#二、环境搭建与快速入门" class="headerlink" title="二、环境搭建与快速入门"></a>二、环境搭建与快速入门</h2><h3 id="2-1-项目结构设计"><a href="#2-1-项目结构设计" class="headerlink" title="2.1 项目结构设计"></a>2.1 项目结构设计</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📁 mybatis-project"] --> B["📁 src/main/java"]    A --> C["📁 src/main/resources"]    B --> D["📁 com.example.mapper"]    B --> E["📁 com.example.entity"]    B --> F["📁 com.example.service"]    B --> G["📁 com.example.util"]    C --> H["📄 mybatis-config.xml"]    C --> I["📄 mapper/UserMapper.xml"]    C --> J["📄 jdbc.properties"]        D --> D1["UserMapper.java"]    D1 --> D2["UserMapper.xml"]        style A fill:#e3f2fd</pre></div><h3 id="2-2-添加-Maven-依赖"><a href="#2-2-添加-Maven-依赖" class="headerlink" title="2.2 添加 Maven 依赖"></a>2.2 添加 Maven 依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0</span></span></span><br><span class="line"><span class="string"><span class="tag">         http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.example<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-demo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mybatis.version</span>&gt;</span>3.5.15<span class="tag">&lt;/<span class="name">mybatis.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- MyBatis 核心依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;mybatis.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- MySQL 驱动 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-j<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>8.0.33<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- JUnit 测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.13.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- Lombok（简化实体类） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.18.30<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 日志框架 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.9<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-simple<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.9<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-3-创建数据库表"><a href="#2-3-创建数据库表" class="headerlink" title="2.3 创建数据库表"></a>2.3 创建数据库表</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建数据库</span></span><br><span class="line"><span class="keyword">CREATE</span> DATABASE IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> mybatis_demo </span><br><span class="line"><span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8mb4_unicode_ci;</span><br><span class="line"></span><br><span class="line">USE mybatis_demo;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 创建用户表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> users (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;用户ID&#x27;</span>,</span><br><span class="line">    username <span class="type">VARCHAR</span>(<span class="number">50</span>) <span class="keyword">NOT NULL</span> <span class="keyword">UNIQUE</span> COMMENT <span class="string">&#x27;用户名&#x27;</span>,</span><br><span class="line">    password <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;密码&#x27;</span>,</span><br><span class="line">    email <span class="type">VARCHAR</span>(<span class="number">100</span>) COMMENT <span class="string">&#x27;邮箱&#x27;</span>,</span><br><span class="line">    phone <span class="type">CHAR</span>(<span class="number">11</span>) COMMENT <span class="string">&#x27;手机号&#x27;</span>,</span><br><span class="line">    status TINYINT <span class="keyword">DEFAULT</span> <span class="number">1</span> COMMENT <span class="string">&#x27;状态：1-正常，0-禁用&#x27;</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">    updated_at <span class="type">TIMESTAMP</span> <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 COMMENT<span class="operator">=</span><span class="string">&#x27;用户表&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 创建文章表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> articles (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;文章ID&#x27;</span>,</span><br><span class="line">    user_id <span class="type">BIGINT</span> <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;作者ID&#x27;</span>,</span><br><span class="line">    title <span class="type">VARCHAR</span>(<span class="number">200</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;标题&#x27;</span>,</span><br><span class="line">    content TEXT COMMENT <span class="string">&#x27;内容&#x27;</span>,</span><br><span class="line">    views <span class="type">INT</span> <span class="keyword">DEFAULT</span> <span class="number">0</span> COMMENT <span class="string">&#x27;浏览量&#x27;</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    updated_at <span class="type">TIMESTAMP</span> <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">FOREIGN KEY</span> (user_id) <span class="keyword">REFERENCES</span> users(id) <span class="keyword">ON</span> <span class="keyword">DELETE</span> CASCADE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 COMMENT<span class="operator">=</span><span class="string">&#x27;文章表&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 插入测试数据</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users (username, password, email, phone) <span class="keyword">VALUES</span></span><br><span class="line">(<span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;$2a$10$xxxx&#x27;</span>, <span class="string">&#x27;admin@example.com&#x27;</span>, <span class="string">&#x27;13800138000&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;test&#x27;</span>, <span class="string">&#x27;$2a$10$yyyy&#x27;</span>, <span class="string">&#x27;test@example.com&#x27;</span>, <span class="string">&#x27;13800138001&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;author1&#x27;</span>, <span class="string">&#x27;$2a$10$zzzz&#x27;</span>, <span class="string">&#x27;author@example.com&#x27;</span>, <span class="string">&#x27;13800138002&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT INTO</span> articles (user_id, title, content, views) <span class="keyword">VALUES</span></span><br><span class="line">(<span class="number">1</span>, <span class="string">&#x27;MyBatis 入门教程&#x27;</span>, <span class="string">&#x27;MyBatis 是一个优秀的持久层框架...&#x27;</span>, <span class="number">100</span>),</span><br><span class="line">(<span class="number">1</span>, <span class="string">&#x27;MyBatis 进阶指南&#x27;</span>, <span class="string">&#x27;本文将深入讲解 MyBatis 的高级特性...&#x27;</span>, <span class="number">200</span>),</span><br><span class="line">(<span class="number">2</span>, <span class="string">&#x27;JDBC vs MyBatis&#x27;</span>, <span class="string">&#x27;对比 JDBC 和 MyBatis 的优缺点...&#x27;</span>, <span class="number">150</span>),</span><br><span class="line">(<span class="number">3</span>, <span class="string">&#x27;MyBatis Plus 使用手册&#x27;</span>, <span class="string">&#x27;MyBatis Plus 是 MyBatis 的增强工具...&#x27;</span>, <span class="number">300</span>);</span><br></pre></td></tr></table></figure><h3 id="2-4-MyBatis-全局配置文件"><a href="#2-4-MyBatis-全局配置文件" class="headerlink" title="2.4 MyBatis 全局配置文件"></a>2.4 MyBatis 全局配置文件</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span> ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">configuration</span> <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Config 3.0//EN&quot;</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 引入外部配置文件（数据库连接信息等） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span> <span class="attr">resource</span>=<span class="string">&quot;jdbc.properties&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 配置 MyBatis 日志输出 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">settings</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 标准日志实现 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;logImpl&quot;</span> <span class="attr">value</span>=<span class="string">&quot;SLF4J&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 开启驼峰命名自动映射：user_id -&gt; userId --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;mapUnderscoreToCamelCase&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 开启延迟加载（按需加载关联对象） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;lazyLoadingEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 设置超时时间（秒） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;defaultStatementTimeout&quot;</span> <span class="attr">value</span>=<span class="string">&quot;30&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 开启二级缓存 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;cacheEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 配置默认枚举类型处理器 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;defaultEnumTypeHandler&quot;</span> <span class="attr">value</span>=<span class="string">&quot;org.apache.ibatis.type.EnumTypeHandler&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 类型别名：简化 XML 中的类名书写 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">typeAliases</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 单个类起别名 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">type</span>=<span class="string">&quot;com.example.entity.User&quot;</span> <span class="attr">alias</span>=<span class="string">&quot;User&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 批量起别名：默认别名是类名（不区分大小写） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.example.entity&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 配置类型处理器 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">typeHandlers</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.example.handler&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">typeHandlers</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 环境配置：可以有多个环境（如开发、测试、生产） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">environments</span> <span class="attr">default</span>=<span class="string">&quot;development&quot;</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 开发环境 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">environment</span> <span class="attr">id</span>=<span class="string">&quot;development&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 事务管理类型：JDBC / MANAGED --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">transactionManager</span> <span class="attr">type</span>=<span class="string">&quot;JDBC&quot;</span>/&gt;</span></span><br><span class="line">            </span><br><span class="line">            <span class="comment">&lt;!-- 数据源类型：UNPOOLED / POOLED / JNDI --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">dataSource</span> <span class="attr">type</span>=<span class="string">&quot;POOLED&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driver&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.driver&#125;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.url&#125;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.username&#125;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>/&gt;</span></span><br><span class="line">                </span><br><span class="line">                <span class="comment">&lt;!-- 连接池配置（POOLED 类型） --&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;poolMaximumActiveConnections&quot;</span> <span class="attr">value</span>=<span class="string">&quot;10&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;poolMaximumIdleConnections&quot;</span> <span class="attr">value</span>=<span class="string">&quot;5&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;poolMaximumCheckoutTime&quot;</span> <span class="attr">value</span>=<span class="string">&quot;20000&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;poolTimeToWait&quot;</span> <span class="attr">value</span>=<span class="string">&quot;20000&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">dataSource</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">environment</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 测试环境 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">environment</span> <span class="attr">id</span>=<span class="string">&quot;test&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">transactionManager</span> <span class="attr">type</span>=<span class="string">&quot;JDBC&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">dataSource</span> <span class="attr">type</span>=<span class="string">&quot;POOLED&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driver&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.driver&#125;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.url&#125;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.username&#125;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">dataSource</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">environment</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">environments</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 映射器：指定 Mapper XML 文件的位置 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mappers</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 方式一：单个 Mapper XML --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">resource</span>=<span class="string">&quot;mapper/UserMapper.xml&quot;</span>/&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 方式二：单个 Mapper 接口（XML 和接口同名的推荐方式） --&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- &lt;mapper class=&quot;com.example.mapper.UserMapper&quot;/&gt; --&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 方式三：批量注册 Mapper 接口 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.example.mapper&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br><span class="line">    </span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-5-数据库连接配置文件"><a href="#2-5-数据库连接配置文件" class="headerlink" title="2.5 数据库连接配置文件"></a>2.5 数据库连接配置文件</h3><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># jdbc.properties</span></span><br><span class="line"><span class="attr">jdbc.driver</span>=<span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="attr">jdbc.url</span>=<span class="string">jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&amp;serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8</span></span><br><span class="line"><span class="attr">jdbc.username</span>=<span class="string">root</span></span><br><span class="line"><span class="attr">jdbc.password</span>=<span class="string">your_password</span></span><br></pre></td></tr></table></figure><hr><h2 id="三、MyBatis-核心组件详解"><a href="#三、MyBatis-核心组件详解" class="headerlink" title="三、MyBatis 核心组件详解"></a>三、MyBatis 核心组件详解</h2><h3 id="3-1-核心组件架构"><a href="#3-1-核心组件架构" class="headerlink" title="3.1 核心组件架构"></a>3.1 核心组件架构</h3><p>MyBatis 的核心组件包括以下几个关键角色：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔧 MyBatis 核心组件"] --> B["📋 SqlSessionFactoryBuilder"]    A --> C["🏭 SqlSessionFactory"]    A --> D["📦 SqlSession"]    A --> E["🗂️ Mapper 接口"]    A --> F["📝 Mapper XML"]        B --> B1["构建 SqlSessionFactory"]    B1 --> C    C -->|每次创建| D    D -->|调用| E    E -->|执行| F        style A fill:#fff3e0    style C fill:#c8e6c9    style D fill:#c8e6c9</pre></div><table><thead><tr><th>组件</th><th>作用</th><th>生命周期</th></tr></thead><tbody><tr><td><strong>SqlSessionFactoryBuilder</strong></td><td>构建 SqlSessionFactory</td><td>应用级别，整个应用只创建一次</td></tr><tr><td><strong>SqlSessionFactory</strong></td><td>创建 SqlSession</td><td>应用级别，应用启动到停止</td></tr><tr><td><strong>SqlSession</strong></td><td>执行 CRUD 操作</td><td>请求级别，每次数据库操作创建</td></tr><tr><td><strong>Mapper 接口</strong></td><td>定义数据操作方法</td><td>由 SqlSession 调用</td></tr><tr><td><strong>Mapper XML</strong></td><td>编写 SQL 语句和映射规则</td><td>由 MyBatis 解析执行</td></tr></tbody></table><h3 id="3-2-实体类创建"><a href="#3-2-实体类创建" class="headerlink" title="3.2 实体类创建"></a>3.2 实体类创建</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"><span class="keyword">import</span> lombok.NoArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.AllArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.Builder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Data</span>             <span class="comment">// Lombok：自动生成 getter/setter/toString</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span> <span class="comment">// 无参构造</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span> <span class="comment">// 全参构造</span></span><br><span class="line"><span class="meta">@Builder</span>          <span class="comment">// 构建者模式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="keyword">private</span> String phone;</span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="keyword">private</span> Date createdAt;</span><br><span class="line">    <span class="keyword">private</span> Date updatedAt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-3-SqlSessionFactory-构建"><a href="#3-3-SqlSessionFactory-构建" class="headerlink" title="3.3 SqlSessionFactory 构建"></a>3.3 SqlSessionFactory 构建</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.util;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.io.Resources;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.session.SqlSessionFactory;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.session.SqlSessionFactoryBuilder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * MyBatis 工具类：管理 SqlSessionFactory</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyBatisUtil</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// SqlSessionFactory 应在整个应用期间只创建一次</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> SqlSessionFactory sqlSessionFactory;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 读取 MyBatis 配置文件</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">resource</span> <span class="operator">=</span> <span class="string">&quot;mybatis-config.xml&quot;</span>;</span><br><span class="line">            <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> Resources.getResourceAsStream(resource);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 构建 SqlSessionFactory</span></span><br><span class="line">            sqlSessionFactory = <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>()</span><br><span class="line">                    .build(inputStream);</span><br><span class="line">            </span><br><span class="line">            System.out.println(<span class="string">&quot;✅ SqlSessionFactory 初始化成功&quot;</span>);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;初始化 SqlSessionFactory 失败&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取 SqlSessionFactory</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> SqlSessionFactory <span class="title function_">getSqlSessionFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sqlSessionFactory;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取 SqlSession</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> SqlSession <span class="title function_">openSession</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sqlSessionFactory.openSession();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取带自动提交的 SqlSession</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> SqlSession <span class="title function_">openSession</span><span class="params">(<span class="type">boolean</span> autoCommit)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sqlSessionFactory.openSession(autoCommit);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-Mapper-接口定义"><a href="#3-4-Mapper-接口定义" class="headerlink" title="3.4 Mapper 接口定义"></a>3.4 Mapper 接口定义</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.mapper;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.entity.User;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.annotations.Param;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户 Mapper 接口</span></span><br><span class="line"><span class="comment"> * MyBatis 会根据接口创建代理对象，代理对象执行 CRUD</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserMapper</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 查询用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    User <span class="title function_">findById</span><span class="params">(Long id)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询所有用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findAll</span><span class="params">()</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 条件查询：用户名模糊匹配 + 状态筛选</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByConditions</span><span class="params">(<span class="meta">@Param(&quot;username&quot;)</span> String username, </span></span><br><span class="line"><span class="params">                                 <span class="meta">@Param(&quot;status&quot;)</span> Integer status)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 多参数查询：使用 Map</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;User&gt; <span class="title function_">findByMap</span><span class="params">(Map&lt;String, Object&gt; params)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">insert</span><span class="params">(User user)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入用户（返回自增主键）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">insertAndReturnId</span><span class="params">(User user)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 更新用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">update</span><span class="params">(User user)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 批量删除</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">batchDelete</span><span class="params">(<span class="meta">@Param(&quot;ids&quot;)</span> List&lt;Long&gt; ids)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 使用 <span class="doctag">@Select</span> 注解定义查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    User <span class="title function_">findByUsername</span><span class="params">(<span class="meta">@Param(&quot;username&quot;)</span> String username)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 统计用户数量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">count</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-5-SqlSession-执行-CRUD"><a href="#3-5-SqlSession-执行-CRUD" class="headerlink" title="3.5 SqlSession 执行 CRUD"></a>3.5 SqlSession 执行 CRUD</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.entity.User;</span><br><span class="line"><span class="keyword">import</span> com.example.mapper.UserMapper;</span><br><span class="line"><span class="keyword">import</span> com.example.util.MyBatisUtil;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.session.SqlSession;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.session.SqlSessionFactory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SqlSessionDemo</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">SqlSessionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> MyBatisUtil.getSqlSessionFactory();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建 SqlSession（每次创建都是新的）</span></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">SqlSession</span> <span class="variable">session</span> <span class="operator">=</span> factory.openSession()) &#123;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 获取 Mapper 接口的代理对象</span></span><br><span class="line">            <span class="type">UserMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> session.getMapper(UserMapper.class);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 调用方法执行查询</span></span><br><span class="line">            <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> mapper.findById(<span class="number">1L</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;查询结果：&quot;</span> + user);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            session.commit();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、CRUD-增删改查操作"><a href="#四、CRUD-增删改查操作" class="headerlink" title="四、CRUD 增删改查操作"></a>四、CRUD 增删改查操作</h2><h3 id="4-1-Mapper-XML-文件结构"><a href="#4-1-Mapper-XML-文件结构" class="headerlink" title="4.1 Mapper XML 文件结构"></a>4.1 Mapper XML 文件结构</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span> ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">mapper</span> <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- namespace 必须与 Mapper 接口全限定名一致 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.example.mapper.UserMapper&quot;</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- ========== 查询操作 ========== --&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- resultType 指定返回类型（单条记录的类型） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        SELECT * FROM users WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 查询所有用户 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        SELECT id, username, password, email, phone, status, </span><br><span class="line">               created_at AS createdAt, updated_at AS updatedAt</span><br><span class="line">        FROM users</span><br><span class="line">        ORDER BY id DESC</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 单参数查询：使用 #&#123;参数名&#125; 占位 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByUsername&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        SELECT * FROM users WHERE username = #&#123;username&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 多参数查询：@Param 注解指定参数名 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByConditions&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        SELECT * FROM users</span><br><span class="line">        WHERE 1=1</span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null and username != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            AND username LIKE CONCAT(&#x27;%&#x27;, #&#123;username&#125;, &#x27;%&#x27;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            AND status = #&#123;status&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        ORDER BY created_at DESC</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 使用 Map 作为参数 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        SELECT * FROM users</span><br><span class="line">        WHERE 1=1</span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null&quot;</span>&gt;</span></span><br><span class="line">            AND username LIKE CONCAT(&#x27;%&#x27;, #&#123;username&#125;, &#x27;%&#x27;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            AND status = #&#123;status&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        ORDER BY id DESC</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 统计查询：返回简单类型 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;count&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;int&quot;</span>&gt;</span></span><br><span class="line">        SELECT COUNT(*) FROM users</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- ========== 插入操作 ========== --&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 插入用户（自增主键会自动返回到对象中） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insert&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        INSERT INTO users (username, password, email, phone, status)</span><br><span class="line">        VALUES (#&#123;username&#125;, #&#123;password&#125;, #&#123;email&#125;, #&#123;phone&#125;, #&#123;status&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 插入并获取自增主键（方式一：useGeneratedKeys） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertAndReturnId&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span> </span></span><br><span class="line"><span class="tag">            <span class="attr">useGeneratedKeys</span>=<span class="string">&quot;true&quot;</span> <span class="attr">keyProperty</span>=<span class="string">&quot;id&quot;</span>&gt;</span></span><br><span class="line">        INSERT INTO users (username, password, email, phone, status)</span><br><span class="line">        VALUES (#&#123;username&#125;, #&#123;password&#125;, #&#123;email&#125;, #&#123;phone&#125;, #&#123;status&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 插入并获取自增主键（方式二：selectKey） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertAndReturnId2&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">selectKey</span> <span class="attr">keyProperty</span>=<span class="string">&quot;id&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;long&quot;</span> <span class="attr">order</span>=<span class="string">&quot;AFTER&quot;</span>&gt;</span></span><br><span class="line">            SELECT LAST_INSERT_ID()</span><br><span class="line">        <span class="tag">&lt;/<span class="name">selectKey</span>&gt;</span></span><br><span class="line">        INSERT INTO users (username, password, email, phone, status)</span><br><span class="line">        VALUES (#&#123;username&#125;, #&#123;password&#125;, #&#123;email&#125;, #&#123;phone&#125;, #&#123;status&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 批量插入 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;batchInsert&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;java.util.List&quot;</span>&gt;</span></span><br><span class="line">        INSERT INTO users (username, password, email, phone, status)</span><br><span class="line">        VALUES</span><br><span class="line">        <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;list&quot;</span> <span class="attr">item</span>=<span class="string">&quot;user&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">            (#&#123;user.username&#125;, #&#123;user.password&#125;, #&#123;user.email&#125;, </span><br><span class="line">             #&#123;user.phone&#125;, #&#123;user.status&#125;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- ========== 更新操作 ========== --&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 更新用户 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">&quot;update&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        UPDATE users</span><br><span class="line">        SET username = #&#123;username&#125;,</span><br><span class="line">            password = #&#123;password&#125;,</span><br><span class="line">            email = #&#123;email&#125;,</span><br><span class="line">            phone = #&#123;phone&#125;,</span><br><span class="line">            status = #&#123;status&#125;</span><br><span class="line">        WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 动态更新（只更新非空字段） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">&quot;dynamicUpdate&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">        UPDATE users</span><br><span class="line">        <span class="tag">&lt;<span class="name">set</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null and username != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">                username = #&#123;username&#125;,</span><br><span class="line">            <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;password != null and password != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">                password = #&#123;password&#125;,</span><br><span class="line">            <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;email != null&quot;</span>&gt;</span></span><br><span class="line">                email = #&#123;email&#125;,</span><br><span class="line">            <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;phone != null&quot;</span>&gt;</span></span><br><span class="line">                phone = #&#123;phone&#125;,</span><br><span class="line">            <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">                status = #&#123;status&#125;,</span><br><span class="line">            <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">set</span>&gt;</span></span><br><span class="line">        WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- ========== 删除操作 ========== --&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 删除用户 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;deleteById&quot;</span>&gt;</span></span><br><span class="line">        DELETE FROM users WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 批量删除 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;batchDelete&quot;</span>&gt;</span></span><br><span class="line">        DELETE FROM users WHERE id IN</span><br><span class="line">        <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;ids&quot;</span> <span class="attr">item</span>=<span class="string">&quot;id&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">            #&#123;id&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br><span class="line">    </span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="4-2-vs-区别"><a href="#4-2-vs-区别" class="headerlink" title="4.2 #{} vs ${} 区别"></a>4.2 #{} vs ${} 区别</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- #&#123;&#125;：预编译参数绑定（推荐，防止 SQL 注入） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 最终执行：SELECT * FROM users WHERE id = ? --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users WHERE id = #&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- $&#123;&#125;：直接字符串替换（存在 SQL 注入风险，仅适用于固定值） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 最终执行：SELECT * FROM users WHERE id = 1 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByIdFixed&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users WHERE id = $&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 使用 $&#123;&#125; 的典型场景：动态表名、排序字段 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users ORDER BY $&#123;sortField&#125; $&#123;sortOrder&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><table><thead><tr><th>特性</th><th>#{}</th><th>${}</th></tr></thead><tbody><tr><td><strong>SQL 编译</strong></td><td>预编译，参数绑定</td><td>直接字符串替换</td></tr><tr><td><strong>SQL 注入</strong></td><td>✅ 安全</td><td>❌ 危险</td></tr><tr><td><strong>性能</strong></td><td>✅ 优（预编译后复用）</td><td>一般</td></tr><tr><td><strong>适用场景</strong></td><td>参数值</td><td>动态列名&#x2F;表名</td></tr></tbody></table><blockquote><p>⚠️ <strong>安全警示</strong>：绝对不要使用 <code>$&#123;&#125;</code> 接收用户输入的原始值，否则可能遭受 SQL 注入攻击！</p></blockquote><h3 id="4-3-结果集映射配置"><a href="#4-3-结果集映射配置" class="headerlink" title="4.3 结果集映射配置"></a>4.3 结果集映射配置</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 方式一：开启驼峰映射（mybatis-config.xml 中配置） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- user_id 自动映射到 userId --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;mapUnderscoreToCamelCase&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 方式二：使用 resultMap 手动映射 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 主键映射 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 普通字段映射 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;password&quot;</span> <span class="attr">column</span>=<span class="string">&quot;password&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;phone&quot;</span> <span class="attr">column</span>=<span class="string">&quot;phone&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;status&quot;</span> <span class="attr">column</span>=<span class="string">&quot;status&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 特殊字段映射：数据库下划线 -&gt; Java 驼峰 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;createdAt&quot;</span> <span class="attr">column</span>=<span class="string">&quot;created_at&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;updatedAt&quot;</span> <span class="attr">column</span>=<span class="string">&quot;updated_at&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 使用 resultMap --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findById&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;UserResultMap&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users WHERE id = #&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 方式三：使用 association 处理一对一关联 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserArticleMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;com.example.entity.UserArticleVO&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 一对一关联：嵌套查询 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">association</span> <span class="attr">property</span>=<span class="string">&quot;latestArticle&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span></span></span><br><span class="line"><span class="tag">                 <span class="attr">select</span>=<span class="string">&quot;com.example.mapper.ArticleMapper.findLatestByUserId&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 方式四：使用 collection 处理一对多关联 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserWithArticlesMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 一对多关联：嵌套查询 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">collection</span> <span class="attr">property</span>=<span class="string">&quot;articles&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">select</span>=<span class="string">&quot;com.example.mapper.ArticleMapper.findByUserId&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="4-4-关联查询实战"><a href="#4-4-关联查询实战" class="headerlink" title="4.4 关联查询实战"></a>4.4 关联查询实战</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 文章实体类</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Article</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> Long userId;</span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    <span class="keyword">private</span> String content;</span><br><span class="line">    <span class="keyword">private</span> Integer views;</span><br><span class="line">    <span class="keyword">private</span> Date createdAt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用户文章 VO</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserArticleVO</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="keyword">private</span> String latestArticleTitle;</span><br><span class="line">    <span class="keyword">private</span> Integer latestArticleViews;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- ArticleMapper.xml --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.example.mapper.ArticleMapper&quot;</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 按用户 ID 查询文章列表 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByUserId&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Article&quot;</span>&gt;</span></span><br><span class="line">        SELECT * FROM articles WHERE user_id = #&#123;userId&#125;</span><br><span class="line">        ORDER BY created_at DESC</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 查询某用户最新文章 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findLatestByUserId&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Article&quot;</span>&gt;</span></span><br><span class="line">        SELECT * FROM articles WHERE user_id = #&#123;userId&#125;</span><br><span class="line">        ORDER BY created_at DESC LIMIT 1</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    </span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- UserMapper.xml：嵌套 select 方式 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserWithArticlesMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 一对多关联：用户有多篇文章 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">collection</span> <span class="attr">property</span>=<span class="string">&quot;articles&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">select</span>=<span class="string">&quot;com.example.mapper.ArticleMapper.findByUserId&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findUserWithArticles&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;UserWithArticlesMap&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users WHERE id = #&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- UserMapper.xml：JOIN 查询方式（一次查询，性能更好） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserWithArticlesMap2&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 一对多关联：嵌套结果集 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">collection</span> <span class="attr">property</span>=<span class="string">&quot;articles&quot;</span> <span class="attr">ofType</span>=<span class="string">&quot;Article&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;article_id&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;title&quot;</span> <span class="attr">column</span>=<span class="string">&quot;title&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;content&quot;</span> <span class="attr">column</span>=<span class="string">&quot;content&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;views&quot;</span> <span class="attr">column</span>=<span class="string">&quot;views&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">collection</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findUserWithArticles2&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;UserWithArticlesMap2&quot;</span>&gt;</span></span><br><span class="line">    SELECT u.id, u.username, u.email,</span><br><span class="line">           a.id AS article_id, a.title, a.content, a.views</span><br><span class="line">    FROM users u</span><br><span class="line">    LEFT JOIN articles a ON u.id = a.user_id</span><br><span class="line">    WHERE u.id = #&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="五、动态-SQL：让-SQL-灵动起来"><a href="#五、动态-SQL：让-SQL-灵动起来" class="headerlink" title="五、动态 SQL：让 SQL 灵动起来"></a>五、动态 SQL：让 SQL 灵动起来</h2><h3 id="5-1-动态-SQL-核心标签"><a href="#5-1-动态-SQL-核心标签" class="headerlink" title="5.1 动态 SQL 核心标签"></a>5.1 动态 SQL 核心标签</h3><p>MyBatis 的动态 SQL 是其最强大的特性之一，可以根据条件动态拼接 SQL：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🔄 动态 SQL 标签"] --> B["if\n条件判断"]    A --> C["where\n智能 WHERE"]    A --> D["set\n智能 SET"]    A --> E["foreach\n循环遍历"]    A --> F["trim\n自定义裁剪"]    A --> G["choose\n选择分支"]        B --> B1["动态添加条件"]    C --> C1["处理多余 AND/OR"]    D --> D1["处理多余 逗号"]    E --> E1["批量操作/IN 查询"]    F --> F1["自定义格式"]    G --> G1["多选一"]        style A fill:#fff3e0</pre></div><h3 id="5-2-if-标签：条件判断"><a href="#5-2-if-标签：条件判断" class="headerlink" title="5.2 if 标签：条件判断"></a>5.2 if 标签：条件判断</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 使用 if 标签动态添加查询条件 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByConditions&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users</span><br><span class="line">    WHERE 1=1</span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null and username.trim() != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">        AND username LIKE CONCAT(&#x27;%&#x27;, #&#123;username&#125;, &#x27;%&#x27;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">        AND status = #&#123;status&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;email != null&quot;</span>&gt;</span></span><br><span class="line">        AND email = #&#123;email&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;createdAfter != null&quot;</span>&gt;</span></span><br><span class="line">        AND created_at <span class="symbol">&amp;gt;</span>= #&#123;createdAfter&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    ORDER BY created_at DESC</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="5-3-where-set-trim-标签"><a href="#5-3-where-set-trim-标签" class="headerlink" title="5.3 where &#x2F; set &#x2F; trim 标签"></a>5.3 where &#x2F; set &#x2F; trim 标签</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- where 标签：自动处理 WHERE 和多余 AND/OR --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByConditions2&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users</span><br><span class="line">    <span class="tag">&lt;<span class="name">where</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null and username != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            AND username LIKE CONCAT(&#x27;%&#x27;, #&#123;username&#125;, &#x27;%&#x27;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            AND status = #&#123;status&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;email != null&quot;</span>&gt;</span></span><br><span class="line">            AND email = #&#123;email&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">where</span>&gt;</span></span><br><span class="line">    ORDER BY created_at DESC</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- set 标签：自动处理 UPDATE 语句中的多余逗号 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">&quot;dynamicUpdate&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    UPDATE users</span><br><span class="line">    <span class="tag">&lt;<span class="name">set</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null and username != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            username = #&#123;username&#125;,</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;password != null and password != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            password = #&#123;password&#125;,</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;email != null&quot;</span>&gt;</span></span><br><span class="line">            email = #&#123;email&#125;,</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;phone != null&quot;</span>&gt;</span></span><br><span class="line">            phone = #&#123;phone&#125;,</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            status = #&#123;status&#125;,</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">set</span>&gt;</span></span><br><span class="line">    WHERE id = #&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- trim 标签：自定义前后缀处理 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 等价于 where 标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">trim</span> <span class="attr">prefix</span>=<span class="string">&quot;WHERE&quot;</span> <span class="attr">prefixOverrides</span>=<span class="string">&quot;AND |OR &quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null&quot;</span>&gt;</span>AND username = #&#123;username&#125;<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">trim</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 等价于 set 标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">trim</span> <span class="attr">prefix</span>=<span class="string">&quot;SET&quot;</span> <span class="attr">suffix</span>=<span class="string">&quot;WHERE id = #&#123;id&#125;&quot;</span> <span class="attr">suffixOverrides</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null&quot;</span>&gt;</span>username = #&#123;username&#125;,<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;email != null&quot;</span>&gt;</span>email = #&#123;email&#125;,<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">trim</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="5-4-foreach-标签：循环遍历"><a href="#5-4-foreach-标签：循环遍历" class="headerlink" title="5.4 foreach 标签：循环遍历"></a>5.4 foreach 标签：循环遍历</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 批量插入 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;batchInsert&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;java.util.List&quot;</span>&gt;</span></span><br><span class="line">    INSERT INTO users (username, password, email, status)</span><br><span class="line">    VALUES</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;list&quot;</span> <span class="attr">item</span>=<span class="string">&quot;user&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">        (#&#123;user.username&#125;, #&#123;user.password&#125;, #&#123;user.email&#125;, #&#123;user.status&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 批量删除（IN 查询） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;batchDelete&quot;</span>&gt;</span></span><br><span class="line">    DELETE FROM users WHERE id IN</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;ids&quot;</span> <span class="attr">item</span>=<span class="string">&quot;id&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">        #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 批量更新状态 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">&quot;batchUpdateStatus&quot;</span>&gt;</span></span><br><span class="line">    UPDATE users SET status = #&#123;status&#125;</span><br><span class="line">    WHERE id IN</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;ids&quot;</span> <span class="attr">item</span>=<span class="string">&quot;id&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">        #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- foreach 的各项属性说明 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">  collection：要遍历的集合/数组/Map</span></span><br><span class="line"><span class="comment">  item：遍历时当前元素的别名</span></span><br><span class="line"><span class="comment">  index：遍历时当前元素的索引（0 开始）</span></span><br><span class="line"><span class="comment">  open：拼接开始字符</span></span><br><span class="line"><span class="comment">  close：拼接结束字符</span></span><br><span class="line"><span class="comment">  separator：元素之间的分隔符</span></span><br><span class="line"><span class="comment">--&gt;</span></span><br></pre></td></tr></table></figure><h3 id="5-5-choose-when-otherwise-标签"><a href="#5-5-choose-when-otherwise-标签" class="headerlink" title="5.5 choose &#x2F; when &#x2F; otherwise 标签"></a>5.5 choose &#x2F; when &#x2F; otherwise 标签</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 搜索条件：可以指定多个搜索维度，但只使用一个</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">searchUsers</span><span class="params">(String type, String keyword)</span> &#123;</span><br><span class="line">    <span class="comment">// type 可以是 &quot;username&quot;、&quot;email&quot;、&quot;phone&quot; 之一</span></span><br><span class="line">    <span class="comment">// keyword 是搜索关键词</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- choose 标签：多选一，类似 switch-case --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;searchUsers&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users</span><br><span class="line">    <span class="tag">&lt;<span class="name">choose</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 优先按用户名搜索 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;type == &#x27;username&#x27; and keyword != null and keyword != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            WHERE username = #&#123;keyword&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 其次按邮箱搜索 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;type == &#x27;email&#x27; and keyword != null and keyword != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            WHERE email = #&#123;keyword&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 再按手机号搜索 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;type == &#x27;phone&#x27; and keyword != null and keyword != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            WHERE phone = #&#123;keyword&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 都不满足则查询所有 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">otherwise</span>&gt;</span></span><br><span class="line">            WHERE status = 1</span><br><span class="line">        <span class="tag">&lt;/<span class="name">otherwise</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">choose</span>&gt;</span></span><br><span class="line">    ORDER BY created_at DESC</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="5-6-动态-SQL-完整示例"><a href="#5-6-动态-SQL-完整示例" class="headerlink" title="5.6 动态 SQL 完整示例"></a>5.6 动态 SQL 完整示例</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 复杂的动态查询：多条件组合，支持排序 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;complexSearch&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users</span><br><span class="line">    <span class="tag">&lt;<span class="name">where</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 用户名模糊搜索 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;username != null and username != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            AND username LIKE CONCAT(&#x27;%&#x27;, #&#123;username&#125;, &#x27;%&#x27;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 状态精确匹配 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            AND status = #&#123;status&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 批量状态查询：status in (1, 2) --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;statusList != null and statusList.size &gt; 0&quot;</span>&gt;</span></span><br><span class="line">            AND status IN</span><br><span class="line">            <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;statusList&quot;</span> <span class="attr">item</span>=<span class="string">&quot;s&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">                #&#123;s&#125;</span><br><span class="line">            <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- 创建时间范围 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;startDate != null&quot;</span>&gt;</span></span><br><span class="line">            AND created_at <span class="symbol">&amp;gt;</span>= #&#123;startDate&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;endDate != null&quot;</span>&gt;</span></span><br><span class="line">            AND created_at <span class="symbol">&amp;lt;</span>= #&#123;endDate&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">&lt;!-- ID 范围 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;minId != null&quot;</span>&gt;</span></span><br><span class="line">            AND id <span class="symbol">&amp;gt;</span>= #&#123;minId&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;maxId != null&quot;</span>&gt;</span></span><br><span class="line">            AND id <span class="symbol">&amp;lt;</span>= #&#123;maxId&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">where</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 动态排序 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">choose</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;sortField == &#x27;username&#x27;&quot;</span>&gt;</span></span><br><span class="line">            ORDER BY username $&#123;sortOrder&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;sortField == &#x27;createdAt&#x27;&quot;</span>&gt;</span></span><br><span class="line">            ORDER BY created_at $&#123;sortOrder&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">otherwise</span>&gt;</span></span><br><span class="line">            ORDER BY id DESC</span><br><span class="line">        <span class="tag">&lt;/<span class="name">otherwise</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">choose</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 分页查询 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;offset != null and limit != null&quot;</span>&gt;</span></span><br><span class="line">        LIMIT #&#123;offset&#125;, #&#123;limit&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="六、映射器配置与自动映射"><a href="#六、映射器配置与自动映射" class="headerlink" title="六、映射器配置与自动映射"></a>六、映射器配置与自动映射</h2><h3 id="6-1-resultMap-详解"><a href="#6-1-resultMap-详解" class="headerlink" title="6.1 resultMap 详解"></a>6.1 resultMap 详解</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 基本 resultMap --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;email&quot;</span> <span class="attr">column</span>=<span class="string">&quot;email&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;status&quot;</span> <span class="attr">column</span>=<span class="string">&quot;status&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 开启驼峰后，以下两行可以省略 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;createdAt&quot;</span> <span class="attr">column</span>=<span class="string">&quot;created_at&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;updatedAt&quot;</span> <span class="attr">column</span>=<span class="string">&quot;updated_at&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 自动映射级别设置 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">  autoMappingBehavior：</span></span><br><span class="line"><span class="comment">  - NONE：禁用自动映射</span></span><br><span class="line"><span class="comment">  - PARTIAL：自动映射除了嵌套结果映射之外的所有列（默认）</span></span><br><span class="line"><span class="comment">  - FULL：自动映射所有列，包括嵌套结果映射</span></span><br><span class="line"><span class="comment">--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;autoMappingBehavior&quot;</span> <span class="attr">value</span>=<span class="string">&quot;PARTIAL&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><h3 id="6-2-关联嵌套查询-vs-嵌套结果查询"><a href="#6-2-关联嵌套查询-vs-嵌套结果查询" class="headerlink" title="6.2 关联嵌套查询 vs 嵌套结果查询"></a>6.2 关联嵌套查询 vs 嵌套结果查询</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["关联查询方式"] --> B["嵌套 select\n（N+1 问题）"]    A --> C["嵌套 result\n（一次查询）"]        B --> B1["执行两次查询\n1次主查询+N次子查询"]    B1 --> B2["子查询独立\n可复用性好"]    B2 --> B3["性能较差\nN+1 问题"]        C --> C1["只执行一次 JOIN\n查询效率高"]    C1 --> C2["主查询包含\n所有字段"]    C2 --> C3["需要处理\n字段重复"]        style B fill:#fff3e0    style C fill:#c8e6c9</pre></div><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 方式一：嵌套 select（会产生 N+1 查询） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 执行：1 次用户查询 + N 次文章查询 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserWithArticlesMap1&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">collection</span> <span class="attr">property</span>=<span class="string">&quot;articles&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">select</span>=<span class="string">&quot;com.example.mapper.ArticleMapper.findByUserId&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 方式二：嵌套 result（推荐，一次查询） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;UserWithArticlesMap2&quot;</span> <span class="attr">type</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;username&quot;</span> <span class="attr">column</span>=<span class="string">&quot;username&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">collection</span> <span class="attr">property</span>=<span class="string">&quot;articles&quot;</span> <span class="attr">ofType</span>=<span class="string">&quot;Article&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;a_id&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;title&quot;</span> <span class="attr">column</span>=<span class="string">&quot;title&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;content&quot;</span> <span class="attr">column</span>=<span class="string">&quot;content&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">collection</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findUserWithArticles2&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;UserWithArticlesMap2&quot;</span>&gt;</span></span><br><span class="line">    SELECT u.id, u.username,</span><br><span class="line">           a.id AS a_id, a.title, a.content</span><br><span class="line">    FROM users u</span><br><span class="line">    LEFT JOIN articles a ON u.id = a.user_id</span><br><span class="line">    WHERE u.id = #&#123;id&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="6-3-discriminator-鉴别器"><a href="#6-3-discriminator-鉴别器" class="headerlink" title="6.3 discriminator 鉴别器"></a>6.3 discriminator 鉴别器</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- discriminator：鉴别器，类似 switch，根据条件使用不同的 resultMap --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;VehicleMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Vehicle&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">&quot;id&quot;</span> <span class="attr">column</span>=<span class="string">&quot;id&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;brand&quot;</span> <span class="attr">column</span>=<span class="string">&quot;brand&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 鉴别器：根据 vehicle_type 字段选择不同的映射 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">discriminator</span> <span class="attr">column</span>=<span class="string">&quot;vehicle_type&quot;</span> <span class="attr">javaType</span>=<span class="string">&quot;string&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 轿车 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">case</span> <span class="attr">value</span>=<span class="string">&quot;car&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;CarResultMap&quot;</span>/&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 卡车 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">case</span> <span class="attr">value</span>=<span class="string">&quot;truck&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;TruckResultMap&quot;</span>/&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 默认使用基础映射 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">case</span> <span class="attr">value</span>=<span class="string">&quot;default&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;BaseVehicleResultMap&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">discriminator</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="七、类型处理器与类型转换"><a href="#七、类型处理器与类型转换" class="headerlink" title="七、类型处理器与类型转换"></a>七、类型处理器与类型转换</h2><h3 id="7-1-内置类型处理器"><a href="#7-1-内置类型处理器" class="headerlink" title="7.1 内置类型处理器"></a>7.1 内置类型处理器</h3><p>MyBatis 内置了大量类型处理器，支持常见数据类型的转换：</p><table><thead><tr><th>类型</th><th>处理器</th></tr></thead><tbody><tr><td><strong>字符串</strong></td><td>StringTypeHandler</td></tr><tr><td><strong>整数</strong></td><td>IntegerTypeHandler</td></tr><tr><td><strong>长整数</strong></td><td>LongTypeHandler</td></tr><tr><td><strong>浮点数</strong></td><td>FloatTypeHandler、DoubleTypeHandler</td></tr><tr><td><strong>布尔值</strong></td><td>BooleanTypeHandler</td></tr><tr><td><strong>日期时间</strong></td><td>DateTypeHandler、TimestampTypeHandler</td></tr><tr><td><strong>字节数组</strong></td><td>ByteArrayTypeHandler</td></tr><tr><td><strong>枚举</strong></td><td>EnumTypeHandler（存储枚举名）、EnumOrdinalTypeHandler（存储枚举序号）</td></tr></tbody></table><h3 id="7-2-自定义类型处理器"><a href="#7-2-自定义类型处理器" class="headerlink" title="7.2 自定义类型处理器"></a>7.2 自定义类型处理器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.handler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.type.JdbcType;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.type.TypeHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义类型处理器：处理 JSON 字符串与 Java Map 的转换</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JsonTypeHandler</span> <span class="keyword">implements</span> <span class="title class_">TypeHandler</span>&lt;Object&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setParameter</span><span class="params">(PreparedStatement ps, <span class="type">int</span> i, Object parameter, </span></span><br><span class="line"><span class="params">                            JdbcType jdbcType)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="comment">// Java 对象 -&gt; JSON 字符串 -&gt; 存入数据库</span></span><br><span class="line">        <span class="keyword">if</span> (parameter == <span class="literal">null</span>) &#123;</span><br><span class="line">            ps.setNull(i, Types.VARCHAR);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> JsonUtil.toJson(parameter);</span><br><span class="line">            ps.setString(i, json);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getResult</span><span class="params">(ResultSet rs, String columnName)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="comment">// JSON 字符串 -&gt; 从数据库读取</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> rs.getString(columnName);</span><br><span class="line">        <span class="keyword">return</span> json == <span class="literal">null</span> ? <span class="literal">null</span> : JsonUtil.fromJson(json, Object.class);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getResult</span><span class="params">(ResultSet rs, <span class="type">int</span> columnIndex)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> rs.getString(columnIndex);</span><br><span class="line">        <span class="keyword">return</span> json == <span class="literal">null</span> ? <span class="literal">null</span> : JsonUtil.fromJson(json, Object.class);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getResult</span><span class="params">(CallableStatement cs, <span class="type">int</span> columnIndex)</span> </span><br><span class="line">            <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> cs.getString(columnIndex);</span><br><span class="line">        <span class="keyword">return</span> json == <span class="literal">null</span> ? <span class="literal">null</span> : JsonUtil.fromJson(json, Object.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-3-注册自定义类型处理器"><a href="#7-3-注册自定义类型处理器" class="headerlink" title="7.3 注册自定义类型处理器"></a>7.3 注册自定义类型处理器</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 在 mybatis-config.xml 中注册 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">typeHandlers</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.example.handler&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">typeHandlers</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 或在 resultMap 中单独指定 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">result</span> <span class="attr">property</span>=<span class="string">&quot;data&quot;</span> <span class="attr">column</span>=<span class="string">&quot;data&quot;</span> <span class="attr">typeHandler</span>=<span class="string">&quot;com.example.handler.JsonTypeHandler&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><h3 id="7-4-枚举类型处理器"><a href="#7-4-枚举类型处理器" class="headerlink" title="7.4 枚举类型处理器"></a>7.4 枚举类型处理器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用户状态枚举</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">UserStatus</span> &#123;</span><br><span class="line">    <span class="comment">// 普通枚举：默认使用 EnumTypeHandler，按名字存储</span></span><br><span class="line">    ACTIVE(<span class="string">&quot;正常&quot;</span>),       <span class="comment">// 存储字符串 &quot;ACTIVE&quot;</span></span><br><span class="line">    INACTIVE(<span class="string">&quot;禁用&quot;</span>),</span><br><span class="line">    DELETED(<span class="string">&quot;已删除&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String desc;</span><br><span class="line">    </span><br><span class="line">    UserStatus(String desc) &#123;</span><br><span class="line">        <span class="built_in">this</span>.desc = desc;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getDesc</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> desc;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 枚举Ordinal：按序号存储</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">OrderStatus</span> &#123;</span><br><span class="line">    PENDING,    <span class="comment">// 存储 0</span></span><br><span class="line">    PAID,        <span class="comment">// 存储 1</span></span><br><span class="line">    SHIPPED,     <span class="comment">// 存储 2</span></span><br><span class="line">    COMPLETED;   <span class="comment">// 存储 3</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 方式一：使用默认的 EnumTypeHandler（存储枚举名） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 数据库中存储：ACTIVE, INACTIVE --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insert&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    INSERT INTO users (username, status) VALUES (#&#123;username&#125;, #&#123;status&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 方式二：自定义枚举处理器（存储中文描述） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insert&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    INSERT INTO users (username, status) VALUES (#&#123;username&#125;, #&#123;statusDesc&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 方式三：使用 EnumOrdinalTypeHandler（存储枚举序号） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 需要在 mybatis-config.xml 中设置 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;defaultEnumTypeHandler&quot;</span> </span></span><br><span class="line"><span class="tag">         <span class="attr">value</span>=<span class="string">&quot;org.apache.ibatis.type.EnumOrdinalTypeHandler&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="八、分页插件与性能优化"><a href="#八、分页插件与性能优化" class="headerlink" title="八、分页插件与性能优化"></a>八、分页插件与性能优化</h2><h3 id="8-1-PageHelper-分页插件"><a href="#8-1-PageHelper-分页插件" class="headerlink" title="8.1 PageHelper 分页插件"></a>8.1 PageHelper 分页插件</h3><p><strong>添加依赖：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.github.pagehelper<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>pagehelper<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>6.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>配置插件：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- mybatis-config.xml --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugin</span> <span class="attr">interceptor</span>=<span class="string">&quot;com.github.pagehelper.PageInterceptor&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- config 参数：插件配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;helperDialect&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;reasonable&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;supportMethodsArguments&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;params&quot;</span> <span class="attr">value</span>=<span class="string">&quot;count=countSql&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>使用分页：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 分页查询示例</span></span><br><span class="line"><span class="keyword">public</span> PageInfo&lt;User&gt; <span class="title function_">findByPage</span><span class="params">(<span class="type">int</span> pageNum, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> (<span class="type">SqlSession</span> <span class="variable">session</span> <span class="operator">=</span> MyBatisUtil.openSession()) &#123;</span><br><span class="line">        <span class="type">UserMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> session.getMapper(UserMapper.class);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 开启分页（必须放在查询方法之前）</span></span><br><span class="line">        PageHelper.startPage(pageNum, pageSize);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 执行查询</span></span><br><span class="line">        List&lt;User&gt; users = mapper.findAll();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 封装分页结果</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageInfo</span>&lt;&gt;(users);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 PageInfo 获取分页信息</span></span><br><span class="line">PageInfo&lt;User&gt; pageInfo = findByPage(<span class="number">1</span>, <span class="number">10</span>);</span><br><span class="line">System.out.println(<span class="string">&quot;当前页：&quot;</span> + pageInfo.getPageNum());</span><br><span class="line">System.out.println(<span class="string">&quot;每页条数：&quot;</span> + pageInfo.getPageSize());</span><br><span class="line">System.out.println(<span class="string">&quot;总记录数：&quot;</span> + pageInfo.getTotal());</span><br><span class="line">System.out.println(<span class="string">&quot;总页数：&quot;</span> + pageInfo.getPages());</span><br><span class="line">System.out.println(<span class="string">&quot;数据：&quot;</span> + pageInfo.getList());</span><br></pre></td></tr></table></figure><h3 id="8-2-性能优化技巧"><a href="#8-2-性能优化技巧" class="headerlink" title="8.2 性能优化技巧"></a>8.2 性能优化技巧</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 1. 使用分页查询避免全表扫描 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findByPage&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users</span><br><span class="line">    <span class="tag">&lt;<span class="name">where</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            status = #&#123;status&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">where</span>&gt;</span></span><br><span class="line">    ORDER BY id DESC</span><br><span class="line">    LIMIT #&#123;offset&#125;, #&#123;limit&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 2. 使用 LIMIT 限制返回条数 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findTop10&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users ORDER BY created_at DESC LIMIT 10</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 3. 使用 EXISTS 替代 IN（子查询） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 低效：IN 子查询可能先执行完再匹配 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findWithIn&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users </span><br><span class="line">    WHERE id IN (SELECT user_id FROM orders WHERE status = &#x27;paid&#x27;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 高效：EXISTS 只要找到一个匹配就停止 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findWithExists&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM users u</span><br><span class="line">    WHERE EXISTS (</span><br><span class="line">        SELECT 1 FROM orders o WHERE o.user_id = u.id AND o.status = &#x27;paid&#x27;</span><br><span class="line">    )</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 4. 批量操作优化：使用 foreach 批量插入 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;batchInsert&quot;</span>&gt;</span></span><br><span class="line">    INSERT INTO users (username, password, email) VALUES</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;list&quot;</span> <span class="attr">item</span>=<span class="string">&quot;user&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">        (#&#123;user.username&#125;, #&#123;user.password&#125;, #&#123;user.email&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 5. 使用延迟加载优化关联查询 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 只有在真正访问关联对象时才查询 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;lazyLoadingEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><h3 id="8-3-MyBatis-日志配置"><a href="#8-3-MyBatis-日志配置" class="headerlink" title="8.3 MyBatis 日志配置"></a>8.3 MyBatis 日志配置</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- mybatis-config.xml --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">settings</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 日志实现（可选：SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | ...） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;logImpl&quot;</span> <span class="attr">value</span>=<span class="string">&quot;SLF4J&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- SQL 日志输出（开发调试用） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;logPrefix&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mybatis-sql&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;logImpl&quot;</span> <span class="attr">value</span>=<span class="string">&quot;org.apache.ibatis.logging.slf4j.Slf4jImpl&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 开启 SQL 日志的简单工具类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SqlLogger</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">logSql</span><span class="params">(String statementId, String sql)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;========== SQL 日志 ==========&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Statement ID: &quot;</span> + statementId);</span><br><span class="line">        System.out.println(<span class="string">&quot;SQL: &quot;</span> + sql);</span><br><span class="line">        System.out.println(<span class="string">&quot;==============================&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、MyBatis-Plus-进阶扩展"><a href="#九、MyBatis-Plus-进阶扩展" class="headerlink" title="九、MyBatis-Plus 进阶扩展"></a>九、MyBatis-Plus 进阶扩展</h2><h3 id="9-1-什么是-MyBatis-Plus？"><a href="#9-1-什么是-MyBatis-Plus？" class="headerlink" title="9.1 什么是 MyBatis-Plus？"></a>9.1 什么是 MyBatis-Plus？</h3><p>MyBatis-Plus（简称 MP）是 MyBatis 的增强工具，在 MyBatis 基础上只做增强不做改变，为简化开发而生。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["MyBatis-Plus"] --> B["🌟 无侵入"]    A --> C["🥰 损耗小"]    A --> D["🛡️ 强大的 CRUD"]    A --> E["⚡ 内置通用 Mapper"]    A --> F["🎮 支持 Lambda"]    A --> G["🔌 支持 ActiveRecord"]        B --> B1["只做增强\n不改变 MyBatis"]    C --> C1["启动即会自动\n注入基本 CRUD"]    D --> D1["内置通用\nService/Mapper"]    E --> E1["通用 Mapper\n拿来即用"]    F --> F1["Lambda 表达式\n编写条件更简单"]    G --> G1["实体类\n即可操作数据库"]</pre></div><h3 id="9-2-MyBatis-Plus-快速入门"><a href="#9-2-MyBatis-Plus-快速入门" class="headerlink" title="9.2 MyBatis-Plus 快速入门"></a>9.2 MyBatis-Plus 快速入门</h3><p><strong>添加依赖：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.baomidou<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-plus-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.5.5<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>配置：</strong></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">your_password</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># MyBatis-Plus 配置</span></span><br><span class="line"><span class="attr">mybatis-plus:</span></span><br><span class="line">  <span class="comment"># Mapper 扫描路径</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath*:/mapper/**/*.xml</span></span><br><span class="line">  <span class="comment"># 类型别名包扫描</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">com.example.entity</span></span><br><span class="line">  <span class="comment"># 开启驼峰</span></span><br><span class="line">  <span class="attr">map-underscore-to-camel-case:</span> <span class="literal">true</span></span><br><span class="line">  <span class="comment"># 日志配置</span></span><br><span class="line">  <span class="attr">configuration:</span></span><br><span class="line">    <span class="attr">log-impl:</span> <span class="string">org.apache.ibatis.logging.slf4j.Slf4jImpl</span></span><br></pre></td></tr></table></figure><p><strong>实体类：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="comment">// @TableName 指定表名（不指定则默认驼峰转下划线）</span></span><br><span class="line"><span class="meta">@TableName(&quot;users&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// @TableId 指定主键（不指定默认使用 id）</span></span><br><span class="line">    <span class="meta">@TableId(type = IdType.AUTO)</span>  <span class="comment">// AUTO: 自增主键</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// @TableField 指定字段映射</span></span><br><span class="line">    <span class="meta">@TableField(&quot;username&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="keyword">private</span> String phone;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 自动填充字段（如创建时间、更新时间）</span></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createdAt;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT_UPDATE)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime updatedAt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Mapper 接口：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 继承 BaseMapper 即可拥有完整的 CRUD 功能</span></span><br><span class="line"><span class="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;User&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 自定义方法</span></span><br><span class="line">    User <span class="title function_">findByUsername</span><span class="params">(String username)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-3-CRUD-示例"><a href="#9-3-CRUD-示例" class="headerlink" title="9.3 CRUD 示例"></a>9.3 CRUD 示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;User&gt; &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ===== 增 =====</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        userMapper.insert(user);  <span class="comment">// 自动插入（忽略主键为 null 的字段）</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addUserIgnoreNull</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        userMapper.insertAllColumn(user);  <span class="comment">// 包括 null 值的字段</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ===== 删 =====</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        userMapper.deleteById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">batchDelete</span><span class="params">(List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">        userMapper.deleteBatchIds(ids);  <span class="comment">// 批量删除</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteByCondition</span><span class="params">(String username)</span> &#123;</span><br><span class="line">        <span class="comment">// 条件构造器删除</span></span><br><span class="line">        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(User::getUsername, username);</span><br><span class="line">        userMapper.delete(wrapper);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ===== 改 =====</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        userMapper.updateById(user);  <span class="comment">// 根据主键更新非 null 字段</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateStatus</span><span class="params">(Long id, Integer status)</span> &#123;</span><br><span class="line">        <span class="comment">// Lambda 更新</span></span><br><span class="line">        LambdaUpdateWrapper&lt;User&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaUpdateWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(User::getId, id)</span><br><span class="line">               .set(User::getStatus, status);</span><br><span class="line">        userMapper.update(<span class="literal">null</span>, wrapper);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ===== 查 =====</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userMapper.selectById(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">getAll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userMapper.selectList(<span class="literal">null</span>);  <span class="comment">// null 表示无条件</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">getByStatus</span><span class="params">(Integer status)</span> &#123;</span><br><span class="line">        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(User::getStatus, status)</span><br><span class="line">               .orderByDesc(User::getCreatedAt);</span><br><span class="line">        <span class="keyword">return</span> userMapper.selectList(wrapper);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> List&lt;User&gt; <span class="title function_">search</span><span class="params">(String keyword)</span> &#123;</span><br><span class="line">        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.likeRight(<span class="string">&quot;username&quot;</span>, keyword)  <span class="comment">// username LIKE &#x27;keyword%&#x27;</span></span><br><span class="line">              .or()</span><br><span class="line">              .likeLeft(<span class="string">&quot;email&quot;</span>, keyword)      <span class="comment">// OR email LIKE &#x27;%keyword&#x27;</span></span><br><span class="line">              .eq(<span class="string">&quot;status&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> userMapper.selectList(wrapper);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Page&lt;User&gt; <span class="title function_">getByPage</span><span class="params">(<span class="type">int</span> pageNum, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">        Page&lt;User&gt; page = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(pageNum, pageSize);</span><br><span class="line">        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(<span class="string">&quot;status&quot;</span>, <span class="number">1</span>)</span><br><span class="line">               .orderByDesc(<span class="string">&quot;created_at&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> userMapper.selectPage(page, wrapper);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-4-Lambda-条件构造器"><a href="#9-4-Lambda-条件构造器" class="headerlink" title="9.4 Lambda 条件构造器"></a>9.4 Lambda 条件构造器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Lambda 条件构造器：使用方法引用，告别字符串硬编码</span></span><br><span class="line">LambdaQueryWrapper&lt;User&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等于</span></span><br><span class="line">wrapper.eq(User::getStatus, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不等于</span></span><br><span class="line">wrapper.ne(User::getStatus, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// LIKE 查询</span></span><br><span class="line">wrapper.like(User::getUsername, <span class="string">&quot;test&quot;</span>);        <span class="comment">// LIKE &#x27;%test%&#x27;</span></span><br><span class="line">wrapper.likeLeft(User::getEmail, <span class="string">&quot;@gmail.com&quot;</span>); <span class="comment">// LIKE &#x27;%@gmail.com&#x27;</span></span><br><span class="line">wrapper.likeRight(User::getEmail, <span class="string">&quot;test&quot;</span>);     <span class="comment">// LIKE &#x27;test%&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// IN 查询</span></span><br><span class="line">wrapper.in(User::getId, Arrays.asList(<span class="number">1L</span>, <span class="number">2L</span>, <span class="number">3L</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// BETWEEN 查询</span></span><br><span class="line">wrapper.between(User::getCreatedAt, startDate, endDate);</span><br><span class="line"></span><br><span class="line"><span class="comment">// IS NULL / IS NOT NULL</span></span><br><span class="line">wrapper.isNull(User::getEmail);</span><br><span class="line">wrapper.isNotNull(User::getPhone);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 排序</span></span><br><span class="line">wrapper.orderByAsc(User::getCreatedAt);</span><br><span class="line">wrapper.orderByDesc(User::getCreatedAt);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分页</span></span><br><span class="line">Page&lt;User&gt; page = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(<span class="number">1</span>, <span class="number">10</span>);</span><br><span class="line">wrapper.orderByDesc(User::getCreatedAt);</span><br><span class="line">Page&lt;User&gt; result = userMapper.selectPage(page, wrapper);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 聚合查询</span></span><br><span class="line">wrapper.select(User::getId, User::getUsername, User::getEmail);</span><br><span class="line">wrapper.groupBy(User::getStatus);</span><br><span class="line">wrapper.having(<span class="string">&quot;COUNT(*) &gt; 1&quot;</span>);</span><br></pre></td></tr></table></figure><hr><h2 id="十、常见问题与最佳实践"><a href="#十、常见问题与最佳实践" class="headerlink" title="十、常见问题与最佳实践"></a>十、常见问题与最佳实践</h2><h3 id="10-1-常见问题与解决方案"><a href="#10-1-常见问题与解决方案" class="headerlink" title="10.1 常见问题与解决方案"></a>10.1 常见问题与解决方案</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["❓ 常见问题"] --> B["SQL 注入"]    A --> C["N+1 查询"]    A --> D["资源泄漏"]    A --> E["事务失效"]        B --> B1["✅ 使用 #{} 占位符\n拒绝 ${} 拼接"]        C --> C1["✅ 使用嵌套 result\n或 batch 查询"]        D --> D1["✅ 使用 try-with-resources\n自动关闭 SqlSession"]        E --> E1["✅ Spring 声明式事务\n@Transactional"]        style A fill:#fff3e0    style B fill:#ffcdd2    style C fill:#fff3e0    style D fill:#e3f2fd    style E fill:#c8e6c9</pre></div><h3 id="10-2-MyBatis-配置清单"><a href="#10-2-MyBatis-配置清单" class="headerlink" title="10.2 MyBatis 配置清单"></a>10.2 MyBatis 配置清单</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- mybatis-config.xml 推荐配置 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">settings</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 开启驼峰命名映射 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;mapUnderscoreToCamelCase&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 开启延迟加载 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;lazyLoadingEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 设置超时时间（秒） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;defaultStatementTimeout&quot;</span> <span class="attr">value</span>=<span class="string">&quot;30&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 开启二级缓存 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;cacheEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 配置日志 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;logImpl&quot;</span> <span class="attr">value</span>=<span class="string">&quot;SLF4J&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="10-3-SQL-编写最佳实践"><a href="#10-3-SQL-编写最佳实践" class="headerlink" title="10.3 SQL 编写最佳实践"></a>10.3 SQL 编写最佳实践</h3><table><thead><tr><th>场景</th><th>推荐做法</th><th>避免做法</th></tr></thead><tbody><tr><td><strong>参数传递</strong></td><td>使用 <code>#&#123;&#125;</code></td><td>手动拼接 <code>$&#123;&#125;</code></td></tr><tr><td><strong>表名&#x2F;列名动态</strong></td><td>使用 <code>$&#123;&#125;</code> 但需白名单校验</td><td>直接使用用户输入</td></tr><tr><td><strong>模糊查询</strong></td><td><code>LIKE CONCAT(&#39;%&#39;, #&#123;val&#125;, &#39;%&#39;)</code></td><td><code>LIKE &#39;%#&#123;val&#125;%&#39;</code></td></tr><tr><td><strong>批量操作</strong></td><td>foreach 批量 SQL</td><td>循环单条执行</td></tr><tr><td><strong>分页</strong></td><td>使用 LIMIT</td><td>全表查询后再分页</td></tr><tr><td><strong>关联查询</strong></td><td>嵌套 result 或 JOIN</td><td>嵌套 select（N+1）</td></tr></tbody></table><h3 id="10-4-Spring-集成-MyBatis"><a href="#10-4-Spring-集成-MyBatis" class="headerlink" title="10.4 Spring 集成 MyBatis"></a>10.4 Spring 集成 MyBatis</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@MapperScan(&quot;com.example.mapper&quot;)</span>  <span class="comment">// 扫描 Mapper 接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyBatisConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionFactory <span class="title function_">sqlSessionFactory</span><span class="params">(</span></span><br><span class="line"><span class="params">            DataSource dataSource)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">SqlSessionFactoryBean</span> <span class="variable">factoryBean</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBean</span>();</span><br><span class="line">        factoryBean.setDataSource(dataSource);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 配置 mapper 位置</span></span><br><span class="line">        factoryBean.setMapperLocations(</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">PathMatchingResourcePatternResolver</span>()</span><br><span class="line">                .getResources(<span class="string">&quot;classpath:/mapper/**/*.xml&quot;</span>)</span><br><span class="line">        );</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 配置类型别名</span></span><br><span class="line">        factoryBean.setTypeAliasesPackage(<span class="string">&quot;com.example.entity&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 配置驼峰映射</span></span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> factoryBean.getObject();</span><br><span class="line">        org.apache.ibatis.session.<span class="type">Configuration</span> <span class="variable">config</span> <span class="operator">=</span> </span><br><span class="line">            factory.getConfiguration();</span><br><span class="line">        config.setMapUnderscoreToCamelCase(<span class="literal">true</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> factory;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Transactional</span>  <span class="comment">// Spring 声明式事务</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">createUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        userMapper.insert(user);</span><br><span class="line">        <span class="comment">// 其他数据库操作...</span></span><br><span class="line">        <span class="comment">// 如果发生异常，事务自动回滚</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="十一、总结"><a href="#十一、总结" class="headerlink" title="十一、总结"></a>十一、总结</h2><h3 id="11-1-核心知识点回顾"><a href="#11-1-核心知识点回顾" class="headerlink" title="11.1 核心知识点回顾"></a>11.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((MyBatis 核心知识))    基础入门      JDBC vs MyBatis      核心组件      环境搭建    CRUD 操作      Mapper 接口      Mapper XML      #{} vs ${}      resultMap 映射    动态 SQL      if/where/set      foreach      choose      trim    关联映射      一对一 association      一对多 collection      嵌套 select      嵌套 result    高级特性      类型处理器      延迟加载      缓存机制    MyBatis-Plus      BaseMapper      Lambda 查询      分页插件      自动填充    性能优化      分页查询      批量操作      N+1 问题      SQL 日志</pre></div><h3 id="11-2-学习路线建议"><a href="#11-2-学习路线建议" class="headerlink" title="11.2 学习路线建议"></a>11.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["第一阶段\n入门"] --> B["第二阶段\n基础CRUD"]    B --> C["第三阶段\n动态SQL"]    C --> D["第四阶段\n关联映射"]    D --> E["第五阶段\n高级特性"]    E --> F["第六阶段\nMyBatis-Plus"]        A --> A1["MyBatis 概述"]    A --> A2["环境搭建"]        B --> B1["Mapper 编写"]    B --> B2["参数映射"]    B --> B3["结果映射"]        C --> C1["if 条件"]    C --> C2["foreach 循环"]    C --> C3["where/set"]        D --> D1["一对一"]    D --> D2["一对多"]    D --> D3["嵌套查询"]        E --> E1["缓存机制"]    E --> E2["延迟加载"]    E --> E3["类型处理器"]        F --> F1["Lambda 查询"]    F --> F2["CRUD 增强"]    F --> F3["分页插件"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0    style E fill:#ffcdd2    style F fill:#e3f2fd</pre></div><h3 id="11-3-下一步推荐学习"><a href="#11-3-下一步推荐学习" class="headerlink" title="11.3 下一步推荐学习"></a>11.3 下一步推荐学习</h3><ul><li>📖 <strong>MyBatis 源码解析</strong>：深入理解核心原理</li><li>📖 <strong>MyBatis-Plus 源码</strong>：学习增强工具的设计思路</li><li>📖 <strong>Spring Data JPA</strong>：对比学习 Hibernate 风格的 ORM</li><li>📖 <strong>泛型 CRUD 封装</strong>：设计通用的 BaseMapper</li><li>📖 <strong>分布式数据库</strong>：学习分库分表、读写分离方案</li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：MyBatis 是 Java 后端开发中承上启下的关键框架——向下封装了 JDBC，向上支撑了 Spring Data 等更高级的 ORM。掌握 MyBatis 的原理和使用，能让你在学习和工作中游刃有余。纸上得来终觉浅，绝知此事要躬行，快动手实践吧！🚀</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/mybatis-complete-guide/</id>
    <link href="https://blog.codenav.top/mybatis-complete-guide/"/>
    <published>2026-05-24T05:33:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="MyBatis-从入门到精通：手把手教你玩转持久层框架-🗂️"><a href="#MyBatis-从入门到精通：手把手教你玩转持久层框架-🗂️" class="headerlink" title="MyBatis 从入门到精通：手把手教你玩转持久层框架 🗂]]>
    </summary>
    <title>MyBatis 从入门到精通：手把手教你玩转持久层框架 🗂️</title>
    <updated>2026-05-24T05:34:22.139Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="数据库" scheme="https://blog.codenav.top/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <category term="JDBC" scheme="https://blog.codenav.top/tags/JDBC/"/>
    <content>
      <![CDATA[<h1 id="JDBC-零基础入门到实战：手把手教你用-Java-操作数据库-☕️"><a href="#JDBC-零基础入门到实战：手把手教你用-Java-操作数据库-☕️" class="headerlink" title="JDBC 零基础入门到实战：手把手教你用 Java 操作数据库 ☕️"></a>JDBC 零基础入门到实战：手把手教你用 Java 操作数据库 ☕️</h1><blockquote><p>JDBC（Java Database Connectivity）是 Java 语言操作数据库的标准 API，理解和掌握 JDBC 是每一个 Java 后端开发者的必修课。本文将从零开始，系统讲解 JDBC 的使用方法、核心概念、进阶技巧以及最佳实践，帮助你真正做到理论与实战相结合！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80jdbc-%E6%A6%82%E8%BF%B0%E4%BB%80%E4%B9%88%E6%98%AF-jdbc">一、JDBC 概述：什么是 JDBC？</a></li><li><a href="#%E4%BA%8Cjdbc-%E9%A9%B1%E5%8A%A8%E7%9A%84%E5%8A%A0%E8%BD%BD%E4%B8%8E%E8%BF%9E%E6%8E%A5">二、JDBC 驱动的加载与连接</a></li><li><a href="#%E4%B8%89jdbc-api-%E6%A0%B8%E5%BF%83%E5%AF%B9%E8%B1%A1">三、JDBC API 核心对象</a></li><li><a href="#%E5%9B%9Bcrud-%E5%AE%9E%E6%88%98%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5">四、CRUD 实战：增删改查</a></li><li><a href="#%E4%BA%94%E9%A2%84%E7%BC%96%E8%AF%91-sql-%E4%B8%8E%E9%98%B2%E6%AD%A2-sql-%E6%B3%A8%E5%85%A5">五、预编译 SQL 与防止 SQL 注入</a></li><li><a href="#%E5%85%AD%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86">六、事务管理</a></li><li><a href="#%E4%B8%83%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0%E8%AF%A6%E8%A7%A3">七、数据库连接池详解</a></li><li><a href="#%E5%85%AB%E5%B0%81%E8%A3%85-basedao-%E9%80%9A%E7%94%A8%E5%B7%A5%E5%85%B7%E7%B1%BB">八、封装 BaseDao 通用工具类</a></li><li><a href="#%E4%B9%9D%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">九、异常处理与最佳实践</a></li><li><a href="#%E5%8D%81%E6%80%BB%E7%BB%93">十、总结</a></li></ul><hr><h2 id="一、JDBC-概述：什么是-JDBC？"><a href="#一、JDBC-概述：什么是-JDBC？" class="headerlink" title="一、JDBC 概述：什么是 JDBC？"></a>一、JDBC 概述：什么是 JDBC？</h2><h3 id="1-1-JDBC-的诞生"><a href="#1-1-JDBC-的诞生" class="headerlink" title="1.1 JDBC 的诞生"></a>1.1 JDBC 的诞生</h3><p>在 JDBC 诞生之前，Java 程序想要操作不同的数据库（比如 MySQL、Oracle、SQL Server），需要针对每种数据库编写不同的代码。这是因为每种数据库都有自己独特的通信协议和数据操作语言。</p><p>JDBC 的出现解决了这个痛点——它提供了一套<strong>统一的数据库操作 API</strong>，开发者只需要学会这一套 API，就能操作任何支持 JDBC 的数据库。</p><h3 id="1-2-JDBC-的工作原理"><a href="#1-2-JDBC-的工作原理" class="headerlink" title="1.2 JDBC 的工作原理"></a>1.2 JDBC 的工作原理</h3><p>JDBC 采用的是<strong>分层架构</strong>，它位于应用程序和数据库之间，充当桥梁的角色：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["☕️ Java 应用层"] --> B["📦 JDBC API"]    B --> C["📋 JDBC 驱动管理器"]    C --> D["🚗 数据库驱动层\n（每种数据库独有）"]    D --> E["🗄️ MySQL\nOracle\nSQL Server\nPostgreSQL ..."]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><p><strong>各层职责：</strong></p><table><thead><tr><th>层级</th><th>组件</th><th>职责</th></tr></thead><tbody><tr><td><strong>应用层</strong></td><td>我们的 Java 代码</td><td>编写业务逻辑，调用 JDBC API</td></tr><tr><td><strong>API 层</strong></td><td><code>java.sql.*</code> 包</td><td>提供统一接口，如 <code>DriverManager</code>、<code>Connection</code>、<code>Statement</code> 等</td></tr><tr><td><strong>驱动管理层</strong></td><td><code>DriverManager</code></td><td>加载驱动，建立连接</td></tr><tr><td><strong>驱动层</strong></td><td>数据库厂商提供的驱动 JAR</td><td>实现 JDBC 接口，负责与数据库通信</td></tr></tbody></table><h3 id="1-3-JDBC-驱动类型"><a href="#1-3-JDBC-驱动类型" class="headerlink" title="1.3 JDBC 驱动类型"></a>1.3 JDBC 驱动类型</h3><p>JDBC 将数据库厂商提供的驱动分为四类：</p><table><thead><tr><th>类型</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>Type 1</strong></td><td>ODBC 桥接驱动，需要本地 ODBC 支持</td><td>JDBC-ODBC Bridge（已淘汰）</td></tr><tr><td><strong>Type 2</strong></td><td>本地 API 驱动，部分 Java，部分 Native</td><td>Oracle Call Interface</td></tr><tr><td><strong>Type 3</strong></td><td>网络协议驱动，中间件转换</td><td>Java Applet 连接中间件</td></tr><tr><td><strong>Type 4</strong></td><td>纯 Java 驱动，直接与数据库通信</td><td>MySQL Connector&#x2F;J、PostgreSQL JDBC Driver</td></tr></tbody></table><blockquote><p>💡 <strong>推荐</strong>：日常开发首选 <strong>Type 4 驱动</strong>（纯 Java 实现），因为它具有平台无关性，只需引入 JAR 包即可使用。</p></blockquote><h3 id="1-4-JDBC-的优势"><a href="#1-4-JDBC-的优势" class="headerlink" title="1.4 JDBC 的优势"></a>1.4 JDBC 的优势</h3><ul><li>✅ <strong>平台无关性</strong>：一次编写，到处运行</li><li>✅ <strong>数据库无关性</strong>：学会一套 API，操作多种数据库</li><li>✅ <strong>标准统一</strong>：Sun 公司制定，所有数据库厂商遵循</li><li>✅ <strong>易于学习</strong>：API 设计简洁，概念清晰</li></ul><hr><h2 id="二、JDBC-驱动的加载与连接"><a href="#二、JDBC-驱动的加载与连接" class="headerlink" title="二、JDBC 驱动的加载与连接"></a>二、JDBC 驱动的加载与连接</h2><h3 id="2-1-环境准备"><a href="#2-1-环境准备" class="headerlink" title="2.1 环境准备"></a>2.1 环境准备</h3><p>在开始之前，你需要准备好以下环境：</p><ol><li>**JDK 1.8+**：确保 Java 开发环境已配置</li><li><strong>MySQL 数据库</strong>：本地或远程 MySQL 服务</li><li><strong>MySQL JDBC 驱动</strong>：<code>mysql-connector-java-8.0.x.jar</code></li><li><strong>IDE</strong>：IntelliJ IDEA 或 Eclipse</li></ol><blockquote><p>📦 <strong>驱动获取</strong>：从 <a href="https://dev.mysql.com/downloads/connector/j/">MySQL 官网</a> 下载对应版本的 Connector&#x2F;J JAR 包。</p></blockquote><h3 id="2-2-创建数据库和表"><a href="#2-2-创建数据库和表" class="headerlink" title="2.2 创建数据库和表"></a>2.2 创建数据库和表</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建数据库</span></span><br><span class="line"><span class="keyword">CREATE</span> DATABASE IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> jdbc_demo <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8mb4_unicode_ci;</span><br><span class="line"></span><br><span class="line">USE jdbc_demo;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 创建用户表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> users (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;用户ID&#x27;</span>,</span><br><span class="line">    username <span class="type">VARCHAR</span>(<span class="number">50</span>) <span class="keyword">NOT NULL</span> <span class="keyword">UNIQUE</span> COMMENT <span class="string">&#x27;用户名&#x27;</span>,</span><br><span class="line">    password <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;密码（加密存储）&#x27;</span>,</span><br><span class="line">    email <span class="type">VARCHAR</span>(<span class="number">100</span>) COMMENT <span class="string">&#x27;邮箱&#x27;</span>,</span><br><span class="line">    status TINYINT <span class="keyword">DEFAULT</span> <span class="number">1</span> COMMENT <span class="string">&#x27;状态：1-正常，0-禁用&#x27;</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">    updated_at <span class="type">TIMESTAMP</span> <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 COMMENT<span class="operator">=</span><span class="string">&#x27;用户表&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 插入测试数据</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users (username, password, email) <span class="keyword">VALUES</span></span><br><span class="line">(<span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;$2a$10$xxxx&#x27;</span>, <span class="string">&#x27;admin@example.com&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;test&#x27;</span>, <span class="string">&#x27;$2a$10$yyyy&#x27;</span>, <span class="string">&#x27;test@example.com&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="2-3-加载-JDBC-驱动"><a href="#2-3-加载-JDBC-驱动" class="headerlink" title="2.3 加载 JDBC 驱动"></a>2.3 加载 JDBC 驱动</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式一：Class.forName() 加载驱动（JDBC 3.0 之前的标准写法）</span></span><br><span class="line"><span class="comment">// 驱动类会自动向 DriverManager 注册</span></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    Class.forName(<span class="string">&quot;com.mysql.cj.jdbc.Driver&quot;</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二：无需手动加载（JDBC 4.0+）</span></span><br><span class="line"><span class="comment">// 从 MySQL Connector/J 8.0 开始，只要驱动 JAR 在 classpath 中，</span></span><br><span class="line"><span class="comment">// 就会自动加载，无需 Class.forName()</span></span><br><span class="line"><span class="comment">// 但为了兼容性和明确性，很多项目仍保留这一行</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式三：使用 ServiceLoader 机制手动加载</span></span><br><span class="line"><span class="comment">// MySQL 驱动的 META-INF/services/java.sql.Driver 文件中声明了驱动类</span></span><br><span class="line"><span class="comment">// Java 程序启动时会自动扫描并加载</span></span><br></pre></td></tr></table></figure><h3 id="2-4-建立数据库连接"><a href="#2-4-建立数据库连接" class="headerlink" title="2.4 建立数据库连接"></a>2.4 建立数据库连接</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.sql.Connection;</span><br><span class="line"><span class="keyword">import</span> java.sql.DriverManager;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConnection</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 数据库连接参数</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&amp;serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">&quot;root&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> <span class="string">&quot;your_password&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 注册驱动（可选，JDBC 4.0+ 自动加载）</span></span><br><span class="line">            Class.forName(<span class="string">&quot;com.mysql.cj.jdbc.Driver&quot;</span>);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 建立连接</span></span><br><span class="line">            connection = DriverManager.getConnection(url, username, password);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 判断连接是否成功</span></span><br><span class="line">            <span class="keyword">if</span> (connection != <span class="literal">null</span>) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;✅ 数据库连接成功！&quot;</span>);</span><br><span class="line">                System.out.println(<span class="string">&quot;连接对象：&quot;</span> + connection);</span><br><span class="line">                System.out.println(<span class="string">&quot;连接 URL：&quot;</span> + connection.getMetaData().getURL());</span><br><span class="line">                System.out.println(<span class="string">&quot;数据库产品：&quot;</span> + connection.getMetaData().getDatabaseProductName());</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 驱动类加载失败：&quot;</span> + e.getMessage());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 数据库连接失败：&quot;</span> + e.getMessage());</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 关闭连接，释放资源</span></span><br><span class="line">            <span class="keyword">if</span> (connection != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    connection.close();</span><br><span class="line">                    System.out.println(<span class="string">&quot;🔌 连接已关闭&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-5-JDBC-URL-详解"><a href="#2-5-JDBC-URL-详解" class="headerlink" title="2.5 JDBC URL 详解"></a>2.5 JDBC URL 详解</h3><p>JDBC URL 是用于定位数据库资源的字符串，格式如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jdbc:mysql://主机名:端口号/数据库名?参数1=值1&amp;参数2=值2</span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>useSSL</code></td><td>是否使用 SSL 连接</td><td><code>true</code> &#x2F; <code>false</code></td></tr><tr><td><code>serverTimezone</code></td><td>服务器时区（必须设置）</td><td><code>Asia/Shanghai</code> &#x2F; <code>UTC</code></td></tr><tr><td><code>characterEncoding</code></td><td>字符编码</td><td><code>utf8</code> &#x2F; <code>utf8mb4</code></td></tr><tr><td><code>useUnicode</code></td><td>是否使用 Unicode</td><td><code>true</code></td></tr><tr><td><code>allowPublicKeyRetrieval</code></td><td>允许公钥检索（MySQL 8.0+）</td><td><code>true</code></td></tr></tbody></table><blockquote><p>⚠️ <strong>常见错误</strong>：如果使用 MySQL 8.0+ 且未设置 <code>serverTimezone</code>，可能报错 <code>The server time zone value &#39;xxx&#39; is unrecognized</code>。</p></blockquote><hr><h2 id="三、JDBC-API-核心对象"><a href="#三、JDBC-API-核心对象" class="headerlink" title="三、JDBC API 核心对象"></a>三、JDBC API 核心对象</h2><h3 id="3-1-四大核心对象概述"><a href="#3-1-四大核心对象概述" class="headerlink" title="3.1 四大核心对象概述"></a>3.1 四大核心对象概述</h3><p>JDBC 的核心操作离不开四个关键对象，它们之间的关系如下：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📋 DriverManager"] --> B["🔗 Connection"]    B --> C["📝 Statement"]    B --> C2["📝 PreparedStatement"]    C --> D["📦 ResultSet"]    C2 --> D    B --> E["🔒 DatabaseMetaData"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style C2 fill:#fff3e0    style D fill:#f8bbd0</pre></div><table><thead><tr><th>对象</th><th>类型</th><th>作用</th></tr></thead><tbody><tr><td><strong>DriverManager</strong></td><td>驱动管理器</td><td>加载驱动、建立连接</td></tr><tr><td><strong>Connection</strong></td><td>接口</td><td>表示数据库连接，负责事务管理</td></tr><tr><td><strong>Statement</strong></td><td>接口</td><td>执行 SQL 语句</td></tr><tr><td><strong>PreparedStatement</strong></td><td>接口</td><td>预编译 SQL，防止 SQL 注入</td></tr><tr><td><strong>ResultSet</strong></td><td>接口</td><td>封装查询结果集</td></tr></tbody></table><h3 id="3-2-DriverManager-详解"><a href="#3-2-DriverManager-详解" class="headerlink" title="3.2 DriverManager 详解"></a>3.2 DriverManager 详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DriverManager 的核心方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 建立连接（最常用）</span></span><br><span class="line"><span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> DriverManager.getConnection(url, username, password);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 建立连接（简化写法，密码直接拼在 URL 中）</span></span><br><span class="line"><span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> DriverManager.getConnection(</span><br><span class="line">    <span class="string">&quot;jdbc:mysql://localhost:3306/jdbc_demo?user=root&amp;password=your_password&quot;</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 设置连接超时时间（单位：秒）</span></span><br><span class="line">DriverManager.setLoginTimeout(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 获取可用的驱动列表</span></span><br><span class="line">java.util.Enumeration&lt;Driver&gt; drivers = DriverManager.getDrivers();</span><br><span class="line"><span class="keyword">while</span> (drivers.hasMoreElements()) &#123;</span><br><span class="line">    <span class="type">Driver</span> <span class="variable">driver</span> <span class="operator">=</span> drivers.nextElement();</span><br><span class="line">    System.out.println(<span class="string">&quot;已注册的驱动：&quot;</span> + driver.getClass().getName());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-3-Connection-详解"><a href="#3-3-Connection-详解" class="headerlink" title="3.3 Connection 详解"></a>3.3 Connection 详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Connection 接口的核心方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 创建 Statement 对象</span></span><br><span class="line"><span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> connection.createStatement();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 创建预编译 SQL 的 PreparedStatement 对象</span></span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> connection.prepareStatement(sql);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 设置事务提交方式</span></span><br><span class="line">connection.setAutoCommit(<span class="literal">false</span>);  <span class="comment">// 手动提交事务</span></span><br><span class="line">connection.setAutoCommit(<span class="literal">true</span>);   <span class="comment">// 自动提交（默认）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 提交事务</span></span><br><span class="line">connection.commit();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 回滚事务</span></span><br><span class="line">connection.rollback();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 创建保存点（支持部分回滚）</span></span><br><span class="line"><span class="type">Savepoint</span> <span class="variable">savepoint</span> <span class="operator">=</span> connection.setSavepoint(<span class="string">&quot;sp1&quot;</span>);</span><br><span class="line">connection.rollback(savepoint);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 7. 获取数据库元数据</span></span><br><span class="line"><span class="type">DatabaseMetaData</span> <span class="variable">metaData</span> <span class="operator">=</span> connection.getMetaData();</span><br><span class="line"><span class="type">String</span> <span class="variable">dbName</span> <span class="operator">=</span> metaData.getDatabaseProductName();</span><br><span class="line"><span class="type">String</span> <span class="variable">dbVersion</span> <span class="operator">=</span> metaData.getDatabaseProductVersion();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 8. 判断是否连接到数据库</span></span><br><span class="line">System.out.println(<span class="string">&quot;是否有效：&quot;</span> + connection.isValid(<span class="number">5</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 9. 设置只读模式（优化性能）</span></span><br><span class="line">connection.setReadOnly(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 10. 设置事务隔离级别</span></span><br><span class="line">connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 11. 关闭连接</span></span><br><span class="line">connection.close();</span><br></pre></td></tr></table></figure><h3 id="3-4-Statement-详解"><a href="#3-4-Statement-详解" class="headerlink" title="3.4 Statement 详解"></a>3.4 Statement 详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Statement 用于执行静态 SQL 语句</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 执行查询（返回 ResultSet）</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql1</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE id = 1&quot;</span>;</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> statement.executeQuery(sql1);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 执行增删改（返回影响的行数）</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql2</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO users (username, password, email) VALUES (&#x27;test&#x27;, &#x27;123456&#x27;, &#x27;test@example.com&#x27;)&quot;</span>;</span><br><span class="line"><span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> statement.executeUpdate(sql2);</span><br><span class="line">System.out.println(<span class="string">&quot;插入了 &quot;</span> + rows + <span class="string">&quot; 行数据&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 执行任意 SQL（返回 boolean，表示是否有 ResultSet）</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql3</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users&quot;</span>;</span><br><span class="line"><span class="type">boolean</span> <span class="variable">hasResult</span> <span class="operator">=</span> statement.execute(sql3);</span><br><span class="line">System.out.println(<span class="string">&quot;是否有结果集：&quot;</span> + hasResult);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 批量执行 SQL</span></span><br><span class="line">statement.addBatch(<span class="string">&quot;INSERT INTO users VALUES (NULL, &#x27;user1&#x27;, &#x27;pass1&#x27;, &#x27;user1@example.com&#x27;)&quot;</span>);</span><br><span class="line">statement.addBatch(<span class="string">&quot;INSERT INTO users VALUES (NULL, &#x27;user2&#x27;, &#x27;pass2&#x27;, &#x27;user2@example.com&#x27;)&quot;</span>);</span><br><span class="line">statement.addBatch(<span class="string">&quot;INSERT INTO users VALUES (NULL, &#x27;user3&#x27;, &#x27;pass3&#x27;, &#x27;user3@example.com&#x27;)&quot;</span>);</span><br><span class="line"><span class="type">int</span>[] batchResults = statement.executeBatch();  <span class="comment">// 返回每条 SQL 影响行数的数组</span></span><br><span class="line">statement.clearBatch();  <span class="comment">// 清空批次</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 获取生成的主键（MySQL 有效）</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql4</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO users (username, password, email) VALUES (&#x27;test&#x27;, &#x27;pass&#x27;, &#x27;test@example.com&#x27;)&quot;</span>;</span><br><span class="line">statement.executeUpdate(sql4, Statement.RETURN_GENERATED_KEYS);</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">keys</span> <span class="operator">=</span> statement.getGeneratedKeys();</span><br><span class="line"><span class="keyword">if</span> (keys.next()) &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">generatedId</span> <span class="operator">=</span> keys.getLong(<span class="number">1</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;生成的主键：&quot;</span> + generatedId);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 获取 ResultSet 的元数据</span></span><br><span class="line"><span class="type">ResultSetMetaData</span> <span class="variable">metaData</span> <span class="operator">=</span> rs.getMetaData();</span><br><span class="line"><span class="type">int</span> <span class="variable">columnCount</span> <span class="operator">=</span> metaData.getColumnCount();</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= columnCount; i++) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;第 &quot;</span> + i + <span class="string">&quot; 列名：&quot;</span> + metaData.getColumnName(i));</span><br><span class="line">    System.out.println(<span class="string">&quot;第 &quot;</span> + i + <span class="string">&quot; 列类型：&quot;</span> + metaData.getColumnTypeName(i));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-5-ResultSet-详解"><a href="#3-5-ResultSet-详解" class="headerlink" title="3.5 ResultSet 详解"></a>3.5 ResultSet 详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ResultSet 封装了查询结果，可以通过游标遍历</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 基本遍历（next() 方法移动游标）</span></span><br><span class="line"><span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">    <span class="comment">// 获取数据（方式一：按列名）</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">id</span> <span class="operator">=</span> rs.getLong(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">email</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;email&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获取数据（方式二：按列索引，从 1 开始）</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">id2</span> <span class="operator">=</span> rs.getLong(<span class="number">1</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">username2</span> <span class="operator">=</span> rs.getString(<span class="number">2</span>);</span><br><span class="line">    </span><br><span class="line">    System.out.println(id + <span class="string">&quot; - &quot;</span> + username + <span class="string">&quot; - &quot;</span> + email);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 获取不同类型的数据</span></span><br><span class="line">rs.getInt(<span class="string">&quot;id&quot;</span>);           <span class="comment">// 整数</span></span><br><span class="line">rs.getLong(<span class="string">&quot;id&quot;</span>);         <span class="comment">// 长整数</span></span><br><span class="line">rs.getDouble(<span class="string">&quot;balance&quot;</span>);  <span class="comment">// 双精度浮点数</span></span><br><span class="line">rs.getBigDecimal(<span class="string">&quot;amt&quot;</span>); <span class="comment">// BigDecimal 精确小数</span></span><br><span class="line">rs.getBoolean(<span class="string">&quot;status&quot;</span>);  <span class="comment">// 布尔值</span></span><br><span class="line">rs.getDate(<span class="string">&quot;created_at&quot;</span>); <span class="comment">// 日期（java.sql.Date）</span></span><br><span class="line">rs.getTime(<span class="string">&quot;login_time&quot;</span>); <span class="comment">// 时间（java.sql.Time）</span></span><br><span class="line">rs.getTimestamp(<span class="string">&quot;updated_at&quot;</span>);  <span class="comment">// 时间戳</span></span><br><span class="line">rs.getString(<span class="string">&quot;content&quot;</span>);  <span class="comment">// 字符串</span></span><br><span class="line">rs.getBytes(<span class="string">&quot;file_data&quot;</span>); <span class="comment">// 字节数组</span></span><br><span class="line">rs.getBlob(<span class="string">&quot;avatar&quot;</span>);     <span class="comment">// 二进制大对象</span></span><br><span class="line">rs.getClob(<span class="string">&quot;description&quot;</span>); <span class="comment">// 字符大对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 判断是否有下一行</span></span><br><span class="line"><span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">    <span class="comment">// 有数据</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 无数据</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 判断游标是否在第一行之前（beforeFirst）之后（afterLast）</span></span><br><span class="line">rs.beforeFirst();  <span class="comment">// 移动到第一行之前</span></span><br><span class="line">rs.afterLast();    <span class="comment">// 移动到最后一行之后</span></span><br><span class="line">rs.first();       <span class="comment">// 移动到第一行</span></span><br><span class="line">rs.last();        <span class="comment">// 移动到最后一行</span></span><br><span class="line">rs.absolute(<span class="number">5</span>);   <span class="comment">// 移动到第 5 行</span></span><br><span class="line">rs.relative(<span class="number">2</span>);   <span class="comment">// 相对移动 2 行</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 获取结果集信息</span></span><br><span class="line"><span class="type">ResultSetMetaData</span> <span class="variable">metaData</span> <span class="operator">=</span> rs.getMetaData();</span><br><span class="line">System.out.println(<span class="string">&quot;总列数：&quot;</span> + metaData.getColumnCount());</span><br><span class="line">System.out.println(<span class="string">&quot;第一列名称：&quot;</span> + metaData.getColumnName(<span class="number">1</span>));</span><br><span class="line">System.out.println(<span class="string">&quot;第一列类型：&quot;</span> + metaData.getColumnTypeName(<span class="number">1</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 判断 ResultSet 是否可滚动（需要创建 Statement 时指定）</span></span><br><span class="line"><span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> connection.createStatement(</span><br><span class="line">    ResultSet.TYPE_FORWARD_ONLY,      <span class="comment">// 游标类型：只能前进</span></span><br><span class="line">    ResultSet.CONCUR_READ_ONLY        <span class="comment">// 并发性：只读</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="type">Statement</span> <span class="variable">stmtScroll</span> <span class="operator">=</span> connection.createStatement(</span><br><span class="line">    ResultSet.TYPE_SCROLL_INSENSITIVE, <span class="comment">// 游标类型：可滚动，不敏感</span></span><br><span class="line">    ResultSet.CONCUR_UPDATABLE         <span class="comment">// 并发性：可更新</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 7. 更新 ResultSet 中的数据（可更新模式下）</span></span><br><span class="line">rs.absolute(<span class="number">1</span>);</span><br><span class="line">rs.updateString(<span class="string">&quot;email&quot;</span>, <span class="string">&quot;new_email@example.com&quot;</span>);</span><br><span class="line">rs.updateRow();  <span class="comment">// 将更新同步到数据库</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 8. 插入新行</span></span><br><span class="line">rs.moveToInsertRow();  <span class="comment">// 移动到插入行</span></span><br><span class="line">rs.updateString(<span class="string">&quot;username&quot;</span>, <span class="string">&quot;new_user&quot;</span>);</span><br><span class="line">rs.updateString(<span class="string">&quot;password&quot;</span>, <span class="string">&quot;new_pass&quot;</span>);</span><br><span class="line">rs.updateString(<span class="string">&quot;email&quot;</span>, <span class="string">&quot;new_user@example.com&quot;</span>);</span><br><span class="line">rs.insertRow();  <span class="comment">// 执行插入</span></span><br></pre></td></tr></table></figure><hr><h2 id="四、CRUD-实战：增删改查"><a href="#四、CRUD-实战：增删改查" class="headerlink" title="四、CRUD 实战：增删改查"></a>四、CRUD 实战：增删改查</h2><h3 id="4-1-项目结构设计"><a href="#4-1-项目结构设计" class="headerlink" title="4.1 项目结构设计"></a>4.1 项目结构设计</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["📁 jdbc-project"] --> B["📁 src/main/java"]    B --> C["📁 com.example.dao"]    B --> D["📁 com.example.entity"]    B --> E["📁 com.example.util"]    B --> F["📁 com.example.test"]    A --> G["📁 src/main/resources"]    G --> H["📄 jdbc.properties"]        C --> C1["UserDao.java"]    D --> D1["User.java"]    E --> E1["JdbcUtil.java"]    F --> F1["UserDaoTest.java"]        style A fill:#e3f2fd    style C fill:#c8e6c9    style D fill:#fff3e0    style E fill:#f8bbd0</pre></div><h3 id="4-2-创建实体类"><a href="#4-2-创建实体类" class="headerlink" title="4.2 创建实体类"></a>4.2 创建实体类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户实体类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="keyword">private</span> Date createdAt;</span><br><span class="line">    <span class="keyword">private</span> Date updatedAt;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">(String username, String password, String email)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.username = username;</span><br><span class="line">        <span class="built_in">this</span>.password = password;</span><br><span class="line">        <span class="built_in">this</span>.email = email;</span><br><span class="line">        <span class="built_in">this</span>.status = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Getter 和 Setter 方法</span></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getUsername</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> username;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUsername</span><span class="params">(String username)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.username = username;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getPassword</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> password;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setPassword</span><span class="params">(String password)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.password = password;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getEmail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> email;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setEmail</span><span class="params">(String email)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.email = email;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getStatus</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> status;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setStatus</span><span class="params">(Integer status)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.status = status;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getCreatedAt</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> createdAt;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCreatedAt</span><span class="params">(Date createdAt)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.createdAt = createdAt;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getUpdatedAt</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> updatedAt;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUpdatedAt</span><span class="params">(Date updatedAt)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.updatedAt = updatedAt;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;User&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;id=&quot;</span> + id +</span><br><span class="line">                <span class="string">&quot;, username=&#x27;&quot;</span> + username + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, password=&#x27;&quot;</span> + password + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, email=&#x27;&quot;</span> + email + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, status=&quot;</span> + status +</span><br><span class="line">                <span class="string">&quot;, createdAt=&quot;</span> + createdAt +</span><br><span class="line">                <span class="string">&quot;, updatedAt=&quot;</span> + updatedAt +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-3-查询单个对象（SELECT-WHERE）"><a href="#4-3-查询单个对象（SELECT-WHERE）" class="headerlink" title="4.3 查询单个对象（SELECT WHERE）"></a>4.3 查询单个对象（SELECT WHERE）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.entity.User;</span><br><span class="line"><span class="keyword">import</span> com.example.util.JdbcUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户 DAO 层：负责用户表的数据操作</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 查询用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">findById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="comment">// SQL 语句</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 获取连接</span></span><br><span class="line">            conn = JdbcUtil.getConnection();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 预编译 SQL</span></span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            ps.setLong(<span class="number">1</span>, id);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行查询</span></span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 遍历结果集</span></span><br><span class="line">            <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                <span class="keyword">return</span> extractUser(rs);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 释放资源</span></span><br><span class="line">            JdbcUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据用户名查询用户</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">findByUsername</span><span class="params">(String username)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE username = ?&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = JdbcUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            ps.setString(<span class="number">1</span>, username);</span><br><span class="line">            </span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                <span class="keyword">return</span> extractUser(rs);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            JdbcUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从 ResultSet 中提取 User 对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> User <span class="title function_">extractUser</span><span class="params">(ResultSet rs)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">        user.setId(rs.getLong(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">        user.setUsername(rs.getString(<span class="string">&quot;username&quot;</span>));</span><br><span class="line">        user.setPassword(rs.getString(<span class="string">&quot;password&quot;</span>));</span><br><span class="line">        user.setEmail(rs.getString(<span class="string">&quot;email&quot;</span>));</span><br><span class="line">        user.setStatus(rs.getInt(<span class="string">&quot;status&quot;</span>));</span><br><span class="line">        user.setCreatedAt(rs.getTimestamp(<span class="string">&quot;created_at&quot;</span>));</span><br><span class="line">        user.setUpdatedAt(rs.getTimestamp(<span class="string">&quot;updated_at&quot;</span>));</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-4-查询集合（SELECT-LIST）"><a href="#4-4-查询集合（SELECT-LIST）" class="headerlink" title="4.4 查询集合（SELECT LIST）"></a>4.4 查询集合（SELECT LIST）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询所有用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findAll</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users ORDER BY id ASC&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        </span><br><span class="line">        rs = ps.executeQuery();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建返回的列表</span></span><br><span class="line">        List&lt;User&gt; users = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 遍历结果集</span></span><br><span class="line">        <span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">            users.add(extractUser(rs));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> users;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(rs, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> Collections.emptyList();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分页查询用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findByPage</span><span class="params">(<span class="type">int</span> pageNum, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users ORDER BY id ASC LIMIT ?, ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 计算分页偏移量</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">offset</span> <span class="operator">=</span> (pageNum - <span class="number">1</span>) * pageSize;</span><br><span class="line">        ps.setInt(<span class="number">1</span>, offset);</span><br><span class="line">        ps.setInt(<span class="number">2</span>, pageSize);</span><br><span class="line">        </span><br><span class="line">        rs = ps.executeQuery();</span><br><span class="line">        </span><br><span class="line">        List&lt;User&gt; users = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">            users.add(extractUser(rs));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> users;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(rs, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> Collections.emptyList();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 条件查询：查询启用状态的用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">findByStatus</span><span class="params">(<span class="type">int</span> status)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE status = ? ORDER BY created_at DESC&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        ps.setInt(<span class="number">1</span>, status);</span><br><span class="line">        </span><br><span class="line">        rs = ps.executeQuery();</span><br><span class="line">        </span><br><span class="line">        List&lt;User&gt; users = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">            users.add(extractUser(rs));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> users;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(rs, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> Collections.emptyList();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-5-插入数据（INSERT）"><a href="#4-5-插入数据（INSERT）" class="headerlink" title="4.5 插入数据（INSERT）"></a>4.5 插入数据（INSERT）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 添加用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">insert</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="comment">// 注意：密码在实际应用中应该加密存储</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO users (username, password, email, status) VALUES (?, ?, ?, ?)&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置参数</span></span><br><span class="line">        ps.setString(<span class="number">1</span>, user.getUsername());</span><br><span class="line">        ps.setString(<span class="number">2</span>, user.getPassword());</span><br><span class="line">        ps.setString(<span class="number">3</span>, user.getEmail());</span><br><span class="line">        ps.setInt(<span class="number">4</span>, user.getStatus() != <span class="literal">null</span> ? user.getStatus() : <span class="number">1</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 执行插入</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取自增主键</span></span><br><span class="line">        <span class="keyword">if</span> (rows &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            rs = ps.getGeneratedKeys();</span><br><span class="line">            <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                <span class="type">long</span> <span class="variable">generatedId</span> <span class="operator">=</span> rs.getLong(<span class="number">1</span>);</span><br><span class="line">                user.setId(generatedId);</span><br><span class="line">                System.out.println(<span class="string">&quot;✅ 生成的主键 ID：&quot;</span> + generatedId);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> rows;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(rs, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 批量插入用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">batchInsert</span><span class="params">(List&lt;User&gt; users)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO users (username, password, email, status) VALUES (?, ?, ?, ?)&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 关闭自动提交，开启手动事务</span></span><br><span class="line">        conn.setAutoCommit(<span class="literal">false</span>);</span><br><span class="line">        </span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (User user : users) &#123;</span><br><span class="line">            ps.setString(<span class="number">1</span>, user.getUsername());</span><br><span class="line">            ps.setString(<span class="number">2</span>, user.getPassword());</span><br><span class="line">            ps.setString(<span class="number">3</span>, user.getEmail());</span><br><span class="line">            ps.setInt(<span class="number">4</span>, user.getStatus() != <span class="literal">null</span> ? user.getStatus() : <span class="number">1</span>);</span><br><span class="line">            ps.addBatch();  <span class="comment">// 添加到批次</span></span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 批量执行</span></span><br><span class="line">        <span class="type">int</span>[] results = ps.executeBatch();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 提交事务</span></span><br><span class="line">        conn.commit();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 计算总影响行数</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">totalRows</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> result : results) &#123;</span><br><span class="line">            <span class="keyword">if</span> (result &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                totalRows += result;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> totalRows;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        <span class="comment">// 发生异常，回滚事务</span></span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.rollback();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException ex) &#123;</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.setAutoCommit(<span class="literal">true</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-6-更新数据（UPDATE）"><a href="#4-6-更新数据（UPDATE）" class="headerlink" title="4.6 更新数据（UPDATE）"></a>4.6 更新数据（UPDATE）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新用户信息</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;UPDATE users SET username = ?, password = ?, email = ?, status = ? WHERE id = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        </span><br><span class="line">        ps.setString(<span class="number">1</span>, user.getUsername());</span><br><span class="line">        ps.setString(<span class="number">2</span>, user.getPassword());</span><br><span class="line">        ps.setString(<span class="number">3</span>, user.getEmail());</span><br><span class="line">        ps.setInt(<span class="number">4</span>, user.getStatus());</span><br><span class="line">        ps.setLong(<span class="number">5</span>, user.getId());</span><br><span class="line">        </span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 更新了 &quot;</span> + rows + <span class="string">&quot; 行数据&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> rows;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新用户状态</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">updateStatus</span><span class="params">(Long userId, <span class="type">int</span> status)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;UPDATE users SET status = ? WHERE id = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        </span><br><span class="line">        ps.setInt(<span class="number">1</span>, status);</span><br><span class="line">        ps.setLong(<span class="number">2</span>, userId);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 批量更新用户状态</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">batchUpdateStatus</span><span class="params">(List&lt;Long&gt; userIds, <span class="type">int</span> status)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (userIds == <span class="literal">null</span> || userIds.isEmpty()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 构建动态 SQL：UPDATE users SET status = ? WHERE id IN (?, ?, ?)</span></span><br><span class="line">    <span class="type">StringBuilder</span> <span class="variable">sqlBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(<span class="string">&quot;UPDATE users SET status = ? WHERE id IN (&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; userIds.size(); i++) &#123;</span><br><span class="line">        sqlBuilder.append(<span class="string">&quot;?&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (i &lt; userIds.size() - <span class="number">1</span>) &#123;</span><br><span class="line">            sqlBuilder.append(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    sqlBuilder.append(<span class="string">&quot;)&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sqlBuilder.toString());</span><br><span class="line">        </span><br><span class="line">        ps.setInt(<span class="number">1</span>, status);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; userIds.size(); i++) &#123;</span><br><span class="line">            ps.setLong(i + <span class="number">2</span>, userIds.get(i));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-7-删除数据（DELETE）"><a href="#4-7-删除数据（DELETE）" class="headerlink" title="4.7 删除数据（DELETE）"></a>4.7 删除数据（DELETE）</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据 ID 删除用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;DELETE FROM users WHERE id = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        ps.setLong(<span class="number">1</span>, id);</span><br><span class="line">        </span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 删除了 &quot;</span> + rows + <span class="string">&quot; 行数据&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> rows;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 批量删除用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">batchDelete</span><span class="params">(List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (ids == <span class="literal">null</span> || ids.isEmpty()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="type">StringBuilder</span> <span class="variable">sqlBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(<span class="string">&quot;DELETE FROM users WHERE id IN (&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; ids.size(); i++) &#123;</span><br><span class="line">        sqlBuilder.append(<span class="string">&quot;?&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (i &lt; ids.size() - <span class="number">1</span>) &#123;</span><br><span class="line">            sqlBuilder.append(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    sqlBuilder.append(<span class="string">&quot;)&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sqlBuilder.toString());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; ids.size(); i++) &#123;</span><br><span class="line">            ps.setLong(i + <span class="number">1</span>, ids.get(i));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 删除所有用户（危险操作！）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">deleteAll</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;DELETE FROM users&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        ps = conn.prepareStatement(sql);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> ps.executeUpdate();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="五、预编译-SQL-与防止-SQL-注入"><a href="#五、预编译-SQL-与防止-SQL-注入" class="headerlink" title="五、预编译 SQL 与防止 SQL 注入"></a>五、预编译 SQL 与防止 SQL 注入</h2><h3 id="5-1-SQL-注入攻击原理"><a href="#5-1-SQL-注入攻击原理" class="headerlink" title="5.1 SQL 注入攻击原理"></a>5.1 SQL 注入攻击原理</h3><p>SQL 注入是 Web 应用中最常见的安全漏洞之一。让我们先看看它是如何发生的：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["👤 用户输入"] --> B["⚠️ 恶意输入"]    B --> C["📝 拼接 SQL"]    C --> D["💥 SQL 注入攻击"]        A --> A1["正常输入"]    A1 --> B1["参数化查询"]    B1 --> D1["✅ 安全执行"]        style A fill:#e3f2fd    style B fill:#ffcdd2    style C fill:#fff3e0    style D fill:#f8bbd0    style B1 fill:#c8e6c9    style D1 fill:#c8e6c9</pre></div><h3 id="5-2-SQL-注入示例"><a href="#5-2-SQL-注入示例" class="headerlink" title="5.2 SQL 注入示例"></a>5.2 SQL 注入示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 危险！使用 Statement 直接拼接 SQL</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">findByUsernameDangerous</span><span class="params">(String username)</span> &#123;</span><br><span class="line">    <span class="comment">// 恶意输入：username = &quot;admin&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE username = &#x27;&quot;</span> + username + <span class="string">&quot;&#x27;&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 实际执行的 SQL：</span></span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE username = &#x27;admin&#x27; OR &#x27;1&#x27;=&#x27;1&#x27;</span></span><br><span class="line">    <span class="comment">// 这会匹配所有用户，攻击者可能借此登录系统！</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 如果用户名是：&#x27;; DROP TABLE users; --</span></span><br><span class="line">    <span class="comment">// 实际执行的 SQL：</span></span><br><span class="line">    <span class="comment">// SELECT * FROM users WHERE username = &#x27;&#x27;; DROP TABLE users; --&#x27;</span></span><br><span class="line">    <span class="comment">// 这会删除整个 users 表！</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-3-预编译-SQL-防注入"><a href="#5-3-预编译-SQL-防注入" class="headerlink" title="5.3 预编译 SQL 防注入"></a>5.3 预编译 SQL 防注入</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 安全！使用 PreparedStatement 预编译 SQL</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">findByUsernameSafe</span><span class="params">(String username)</span> &#123;</span><br><span class="line">    <span class="comment">// PreparedStatement 会对特殊字符转义</span></span><br><span class="line">    <span class="comment">// 输入：admin&#x27; OR &#x27;1&#x27;=&#x27;1</span></span><br><span class="line">    <span class="comment">// 会被当作普通字符串处理，不会影响 SQL 结构</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE username = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 参数绑定后，即使输入包含 SQL 语句，也不会被执行</span></span><br><span class="line">    <span class="comment">// 最终只会查找用户名为 &quot;admin&#x27; OR &#x27;1&#x27;=&#x27;1&quot; 的用户（不存在）</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-4-PreparedStatement-优势总结"><a href="#5-4-PreparedStatement-优势总结" class="headerlink" title="5.4 PreparedStatement 优势总结"></a>5.4 PreparedStatement 优势总结</h3><table><thead><tr><th>特性</th><th>Statement</th><th>PreparedStatement</th></tr></thead><tbody><tr><td><strong>SQL 拼接</strong></td><td>手动拼接字符串</td><td>参数绑定 <code>?</code> 占位符</td></tr><tr><td><strong>SQL 注入</strong></td><td>❌ 容易攻击</td><td>✅ 安全防护</td></tr><tr><td><strong>性能</strong></td><td>每次编译新 SQL</td><td>预编译，重复执行效率高</td></tr><tr><td><strong>可读性</strong></td><td>拼接复杂，难维护</td><td>代码清晰，参数分明</td></tr><tr><td><strong>类型安全</strong></td><td>全部 String</td><td>支持多种数据类型绑定</td></tr></tbody></table><h3 id="5-5-预编译-SQL-参数绑定详解"><a href="#5-5-预编译-SQL-参数绑定详解" class="headerlink" title="5.5 预编译 SQL 参数绑定详解"></a>5.5 预编译 SQL 参数绑定详解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO users (username, password, email, status, balance, created_at) &quot;</span> +</span><br><span class="line">              <span class="string">&quot;VALUES (?, ?, ?, ?, ?, ?)&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> conn.prepareStatement(sql);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置参数（按问号顺序，从 1 开始）</span></span><br><span class="line">ps.setString(<span class="number">1</span>, <span class="string">&quot;testuser&quot;</span>);           <span class="comment">// 字符串</span></span><br><span class="line">ps.setString(<span class="number">2</span>, <span class="string">&quot;hashed_password&quot;</span>);     <span class="comment">// 字符串</span></span><br><span class="line">ps.setString(<span class="number">3</span>, <span class="string">&quot;test@example.com&quot;</span>);    <span class="comment">// 字符串</span></span><br><span class="line">ps.setInt(<span class="number">4</span>, <span class="number">1</span>);                        <span class="comment">// 整数</span></span><br><span class="line">ps.setBigDecimal(<span class="number">5</span>, <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">&quot;100.50&quot;</span>));  <span class="comment">// BigDecimal</span></span><br><span class="line">ps.setTimestamp(<span class="number">6</span>, <span class="keyword">new</span> <span class="title class_">Timestamp</span>(System.currentTimeMillis()));  <span class="comment">// 时间戳</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果某个字段允许 NULL</span></span><br><span class="line">ps.setNull(<span class="number">4</span>, Types.INTEGER);  <span class="comment">// 设置为 NULL，并指定 SQL 类型</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 批量设置参数</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; users.size(); i++) &#123;</span><br><span class="line">    <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> users.get(i);</span><br><span class="line">    ps.setString(<span class="number">1</span>, user.getUsername());</span><br><span class="line">    ps.setString(<span class="number">2</span>, user.getPassword());</span><br><span class="line">    ps.setString(<span class="number">3</span>, user.getEmail());</span><br><span class="line">    ps.setInt(<span class="number">4</span>, user.getStatus());</span><br><span class="line">    ps.addBatch();</span><br><span class="line">&#125;</span><br><span class="line">ps.executeBatch();</span><br></pre></td></tr></table></figure><hr><h2 id="六、事务管理"><a href="#六、事务管理" class="headerlink" title="六、事务管理"></a>六、事务管理</h2><h3 id="6-1-JDBC-事务基本操作"><a href="#6-1-JDBC-事务基本操作" class="headerlink" title="6.1 JDBC 事务基本操作"></a>6.1 JDBC 事务基本操作</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 转账示例：事务的典型应用场景</span></span><br><span class="line"><span class="comment"> * 从 A 账户转出 1000 元到 B 账户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">transfer</span><span class="params">(Long fromUserId, Long toUserId, BigDecimal amount)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">deductSql</span> <span class="operator">=</span> <span class="string">&quot;UPDATE accounts SET balance = balance - ? WHERE user_id = ? AND balance &gt;= ?&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">addSql</span> <span class="operator">=</span> <span class="string">&quot;UPDATE accounts SET balance = balance + ? WHERE user_id = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">deductPs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">addPs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 关闭自动提交，开启手动事务</span></span><br><span class="line">        conn.setAutoCommit(<span class="literal">false</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第一步：扣款</span></span><br><span class="line">        deductPs = conn.prepareStatement(deductSql);</span><br><span class="line">        deductPs.setBigDecimal(<span class="number">1</span>, amount);</span><br><span class="line">        deductPs.setLong(<span class="number">2</span>, fromUserId);</span><br><span class="line">        deductPs.setBigDecimal(<span class="number">3</span>, amount);</span><br><span class="line">        <span class="type">int</span> <span class="variable">affectedRows</span> <span class="operator">=</span> deductPs.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 如果扣款失败（余额不足），回滚事务</span></span><br><span class="line">        <span class="keyword">if</span> (affectedRows == <span class="number">0</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 余额不足，转账失败&quot;</span>);</span><br><span class="line">            conn.rollback();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第二步：充值</span></span><br><span class="line">        addPs = conn.prepareStatement(addSql);</span><br><span class="line">        addPs.setBigDecimal(<span class="number">1</span>, amount);</span><br><span class="line">        addPs.setLong(<span class="number">2</span>, toUserId);</span><br><span class="line">        <span class="type">int</span> <span class="variable">addRows</span> <span class="operator">=</span> addPs.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (addRows == <span class="number">0</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;❌ 目标账户不存在，转账失败&quot;</span>);</span><br><span class="line">            conn.rollback();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第三步：提交事务</span></span><br><span class="line">        conn.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 转账成功！&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;❌ 转账异常：&quot;</span> + e.getMessage());</span><br><span class="line">        <span class="comment">// 发生异常，回滚事务</span></span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.rollback();</span><br><span class="line">                System.out.println(<span class="string">&quot;🔄 事务已回滚&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException ex) &#123;</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 恢复自动提交状态</span></span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.setAutoCommit(<span class="literal">true</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 关闭资源</span></span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, deductPs, <span class="literal">null</span>);</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, addPs, conn);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-2-保存点实现部分回滚"><a href="#6-2-保存点实现部分回滚" class="headerlink" title="6.2 保存点实现部分回滚"></a>6.2 保存点实现部分回滚</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 带有保存点的事务：可以回滚到指定点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transactionWithSavepoint</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql1</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO orders (user_id, total_amount) VALUES (?, ?)&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql2</span> <span class="operator">=</span> <span class="string">&quot;UPDATE users SET status = 0 WHERE id = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps1</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps2</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">Savepoint</span> <span class="variable">savepoint</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        conn.setAutoCommit(<span class="literal">false</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第一步：创建订单</span></span><br><span class="line">        ps1 = conn.prepareStatement(sql1);</span><br><span class="line">        ps1.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line">        ps1.setBigDecimal(<span class="number">2</span>, <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">&quot;500.00&quot;</span>));</span><br><span class="line">        ps1.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置保存点</span></span><br><span class="line">        savepoint = conn.setSavepoint(<span class="string">&quot;after_create_order&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;📍 保存点已创建：after_create_order&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 第二步：更新用户状态</span></span><br><span class="line">        ps2 = conn.prepareStatement(sql2);</span><br><span class="line">        ps2.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line">        ps2.executeUpdate();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 提交事务</span></span><br><span class="line">        conn.commit();</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 事务提交成功&quot;</span>);</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        <span class="keyword">if</span> (savepoint != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 回滚到保存点（保留保存点之前的操作）</span></span><br><span class="line">                conn.rollback(savepoint);</span><br><span class="line">                System.out.println(<span class="string">&quot;🔄 已回滚到保存点：after_create_order&quot;</span>);</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 手动提交，保留保存点之前的操作</span></span><br><span class="line">                conn.commit();</span><br><span class="line">                System.out.println(<span class="string">&quot;✅ 部分操作已提交&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException ex) &#123;</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.rollback();</span><br><span class="line">                System.out.println(<span class="string">&quot;🔄 全事务回滚&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException ex) &#123;</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.setAutoCommit(<span class="literal">true</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps1, <span class="literal">null</span>);</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, ps2, conn);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-事务隔离级别设置"><a href="#6-3-事务隔离级别设置" class="headerlink" title="6.3 事务隔离级别设置"></a>6.3 事务隔离级别设置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 设置事务隔离级别</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setTransactionIsolationLevel</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = JdbcUtil.getConnection();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// MySQL 支持的隔离级别：</span></span><br><span class="line">        <span class="comment">// Connection.TRANSACTION_READ_UNCOMMITTED</span></span><br><span class="line">        <span class="comment">// Connection.TRANSACTION_READ_COMMITTED</span></span><br><span class="line">        <span class="comment">// Connection.TRANSACTION_REPEATABLE_READ（MySQL 默认）</span></span><br><span class="line">        <span class="comment">// Connection.TRANSACTION_SERIALIZABLE</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置为可重复读（REPEATABLE READ）</span></span><br><span class="line">        conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;当前隔离级别：&quot;</span> + getIsolationLevelName(conn.getTransactionIsolation()));</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        JdbcUtil.close(<span class="literal">null</span>, <span class="literal">null</span>, conn);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> String <span class="title function_">getIsolationLevelName</span><span class="params">(<span class="type">int</span> level)</span> &#123;</span><br><span class="line">    <span class="keyword">switch</span> (level) &#123;</span><br><span class="line">        <span class="keyword">case</span> Connection.TRANSACTION_READ_UNCOMMITTED:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;READ_UNCOMMITTED&quot;</span>;</span><br><span class="line">        <span class="keyword">case</span> Connection.TRANSACTION_READ_COMMITTED:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;READ_COMMITTED&quot;</span>;</span><br><span class="line">        <span class="keyword">case</span> Connection.TRANSACTION_REPEATABLE_READ:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;REPEATABLE_READ&quot;</span>;</span><br><span class="line">        <span class="keyword">case</span> Connection.TRANSACTION_SERIALIZABLE:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;SERIALIZABLE&quot;</span>;</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;UNKNOWN&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="七、数据库连接池详解"><a href="#七、数据库连接池详解" class="headerlink" title="七、数据库连接池详解"></a>七、数据库连接池详解</h2><h3 id="7-1-什么是数据库连接池？"><a href="#7-1-什么是数据库连接池？" class="headerlink" title="7.1 什么是数据库连接池？"></a>7.1 什么是数据库连接池？</h3><p>传统 JDBC 每次操作数据库都需要建立新的连接，操作完成后关闭连接。这种方式在高并发场景下性能很差，因为建立 TCP 连接是一个耗时的操作。</p><p><strong>连接池</strong>的思想是：<strong>预先创建一定数量的数据库连接，放置在内存中备用，用完归还而非关闭。</strong></p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["❌ 传统 JDBC"] --> A1["每次请求\n建立新连接"]    A1 --> A2["耗时\n2-3秒"]        B["✅ 连接池"] --> B1["预先创建\n连接队列"]    B1 --> B2["快速获取\n0.1秒"]        style A fill:#ffcdd2    style A1 fill:#ffcdd2    style A2 fill:#ffcdd2    style B fill:#c8e6c9    style B1 fill:#c8e6c9    style B2 fill:#c8e6c9</pre></div><h3 id="7-2-常见连接池对比"><a href="#7-2-常见连接池对比" class="headerlink" title="7.2 常见连接池对比"></a>7.2 常见连接池对比</h3><table><thead><tr><th>连接池</th><th>优点</th><th>缺点</th><th>推荐场景</th></tr></thead><tbody><tr><td><strong>Druid（阿里）</strong></td><td>功能丰富，监控强大</td><td>文档较少</td><td>国内项目首选</td></tr><tr><td><strong>HikariCP</strong></td><td>性能最高，Spring Boot 2.x 默认</td><td>功能相对简单</td><td>高性能需求</td></tr><tr><td><strong>C3P0</strong></td><td>发展成熟</td><td>性能一般，已停止维护</td><td>老项目</td></tr><tr><td><strong>DBCP</strong></td><td>稳定</td><td>配置繁琐</td><td>偶有使用</td></tr></tbody></table><blockquote><p>💡 <strong>推荐</strong>：阿里的 <strong>Druid</strong> 连接池在国内使用最广，提供了强大的监控和 SQL 防注入功能；Spring Boot 2.x 默认使用 <strong>HikariCP</strong>，性能最优。</p></blockquote><h3 id="7-3-使用-Druid-连接池"><a href="#7-3-使用-Druid-连接池" class="headerlink" title="7.3 使用 Druid 连接池"></a>7.3 使用 Druid 连接池</h3><p><strong>添加依赖（ Maven）：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>druid<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.18<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>配置文件 <code>druid.properties</code>：</strong></p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 数据库连接参数</span></span><br><span class="line"><span class="attr">driverClassName</span>=<span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="attr">url</span>=<span class="string">jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line"><span class="attr">username</span>=<span class="string">root</span></span><br><span class="line"><span class="attr">password</span>=<span class="string">your_password</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 连接池配置</span></span><br><span class="line"><span class="attr">initialSize</span>=<span class="string">5              # 初始连接数</span></span><br><span class="line"><span class="attr">maxActive</span>=<span class="string">20              # 最大活跃连接数</span></span><br><span class="line"><span class="attr">maxWait</span>=<span class="string">3000              # 获取连接最大等待时间（毫秒）</span></span><br><span class="line"><span class="attr">minIdle</span>=<span class="string">5                # 最小空闲连接数</span></span><br><span class="line"><span class="attr">timeBetweenEvictionRunsMillis</span>=<span class="string">60000   # 清理线程运行间隔</span></span><br><span class="line"><span class="attr">minEvictableIdleTimeMillis</span>=<span class="string">300000    # 最小空闲时间</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 测试连接</span></span><br><span class="line"><span class="attr">validationQuery</span>=<span class="string">SELECT 1  # 验证连接的 SQL</span></span><br><span class="line"><span class="attr">testWhileIdle</span>=<span class="string">true        # 空闲时测试连接</span></span><br><span class="line"><span class="attr">testOnBorrow</span>=<span class="string">false        # 借出时测试（影响性能）</span></span><br><span class="line"><span class="attr">testOnReturn</span>=<span class="string">false        # 归还时测试</span></span><br></pre></td></tr></table></figure><p><strong>创建 Druid 连接池工具类：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.util;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.druid.pool.DruidDataSource;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.druid.pool.DruidDataSourceFactory;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.druid.pool.DruidPooledConnection;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.sql.DataSource;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.sql.Connection;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLException;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Druid 连接池工具类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DruidUtil</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 数据源（单例）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> DruidDataSource dataSource;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 线程本地容器：存放每个线程的连接</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;Connection&gt; threadLocal = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 加载配置文件</span></span><br><span class="line">            <span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">            <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> DruidUtil.class.getClassLoader()</span><br><span class="line">                    .getResourceAsStream(<span class="string">&quot;druid.properties&quot;</span>);</span><br><span class="line">            properties.load(is);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 创建数据源</span></span><br><span class="line">            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);</span><br><span class="line">            </span><br><span class="line">            System.out.println(<span class="string">&quot;✅ Druid 连接池初始化成功&quot;</span>);</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;初始化 Druid 连接池失败&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取数据源</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> DataSource <span class="title function_">getDataSource</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从连接池获取连接</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title function_">getConnection</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="comment">// 先从 ThreadLocal 获取</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> threadLocal.get();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (conn == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 从连接池获取</span></span><br><span class="line">            conn = dataSource.getConnection();</span><br><span class="line">            <span class="comment">// 存入 ThreadLocal</span></span><br><span class="line">            threadLocal.set(conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> conn;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 开启事务</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">beginTransaction</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> getConnection();</span><br><span class="line">        conn.setAutoCommit(<span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 提交事务</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">commit</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> threadLocal.get();</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            conn.commit();</span><br><span class="line">            conn.setAutoCommit(<span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 回滚事务</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">rollback</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> threadLocal.get();</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            conn.rollback();</span><br><span class="line">            conn.setAutoCommit(<span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 关闭连接（归还到连接池）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(Connection conn)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                conn.close();  <span class="comment">// 归还到连接池，而非真正关闭</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 统一关闭资源</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(AutoCloseable... resources)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (AutoCloseable resource : resources) &#123;</span><br><span class="line">            <span class="keyword">if</span> (resource != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    resource.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取连接数信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">printPoolStatus</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;活跃连接数：&quot;</span> + dataSource.getActiveCount());</span><br><span class="line">        System.out.println(<span class="string">&quot;空闲连接数：&quot;</span> + dataSource.getPool().getIdleCount());</span><br><span class="line">        System.out.println(<span class="string">&quot;等待获取连接的线程数：&quot;</span> + dataSource.getWaitThreadCount());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>使用 Druid 连接池：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式一：直接使用</span></span><br><span class="line"><span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> DruidUtil.getConnection();</span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> conn.prepareStatement(sql);</span><br><span class="line">ps.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> ps.executeQuery();</span><br><span class="line"><span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;用户名：&quot;</span> + rs.getString(<span class="string">&quot;username&quot;</span>));</span><br><span class="line">&#125;</span><br><span class="line">DruidUtil.close(rs, ps, conn);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二：开启事务后使用</span></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    DruidUtil.beginTransaction();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 执行多个操作，共用同一个连接</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql1</span> <span class="operator">=</span> <span class="string">&quot;INSERT INTO orders (user_id, total_amount) VALUES (?, ?)&quot;</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps1</span> <span class="operator">=</span> DruidUtil.getConnection().prepareStatement(sql1);</span><br><span class="line">    ps1.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line">    ps1.setBigDecimal(<span class="number">2</span>, <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">&quot;100.00&quot;</span>));</span><br><span class="line">    ps1.executeUpdate();</span><br><span class="line">    </span><br><span class="line">    <span class="type">String</span> <span class="variable">sql2</span> <span class="operator">=</span> <span class="string">&quot;UPDATE users SET status = 0 WHERE id = ?&quot;</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps2</span> <span class="operator">=</span> DruidUtil.getConnection().prepareStatement(sql2);</span><br><span class="line">    ps2.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line">    ps2.executeUpdate();</span><br><span class="line">    </span><br><span class="line">    DruidUtil.commit();</span><br><span class="line">    System.out.println(<span class="string">&quot;✅ 事务提交成功&quot;</span>);</span><br><span class="line">    </span><br><span class="line">&#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">    DruidUtil.rollback();</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="7-4-使用-HikariCP-连接池"><a href="#7-4-使用-HikariCP-连接池" class="headerlink" title="7.4 使用 HikariCP 连接池"></a>7.4 使用 HikariCP 连接池</h3><p><strong>添加依赖：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.zaxxer<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>HikariCP<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>5.0.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>配置类：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.zaxxer.hikari.HikariConfig;</span><br><span class="line"><span class="keyword">import</span> com.zaxxer.hikari.HikariDataSource;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.sql.DataSource;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HikariConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> DataSource <span class="title function_">dataSource</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">HikariConfig</span> <span class="variable">config</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HikariConfig</span>();</span><br><span class="line">        </span><br><span class="line">        config.setJdbcUrl(<span class="string">&quot;jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&amp;serverTimezone=Asia/Shanghai&quot;</span>);</span><br><span class="line">        config.setUsername(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        config.setPassword(<span class="string">&quot;your_password&quot;</span>);</span><br><span class="line">        config.setDriverClassName(<span class="string">&quot;com.mysql.cj.jdbc.Driver&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 连接池配置</span></span><br><span class="line">        config.setMaximumPoolSize(<span class="number">10</span>);    <span class="comment">// 最大连接数</span></span><br><span class="line">        config.setMinimumIdle(<span class="number">5</span>);         <span class="comment">// 最小空闲连接</span></span><br><span class="line">        config.setIdleTimeout(<span class="number">600000</span>);     <span class="comment">// 空闲超时</span></span><br><span class="line">        config.setConnectionTimeout(<span class="number">30000</span>); <span class="comment">// 连接超时</span></span><br><span class="line">        config.setMaxLifetime(<span class="number">1800000</span>);    <span class="comment">// 最大生命周期</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">HikariDataSource</span>(config);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="八、封装-BaseDao-通用工具类"><a href="#八、封装-BaseDao-通用工具类" class="headerlink" title="八、封装 BaseDao 通用工具类"></a>八、封装 BaseDao 通用工具类</h2><h3 id="8-1-BaseDao-设计思路"><a href="#8-1-BaseDao-设计思路" class="headerlink" title="8.1 BaseDao 设计思路"></a>8.1 BaseDao 设计思路</h3><p>为了减少重复代码，我们可以将 JDBC 的通用操作抽取到 BaseDao 中：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["🧱 BaseDao"] --> B["T findById\n(Long id)"]    A --> C["List<T> findAll\n()"]    A --> D["int insert\n(T entity)"]    A --> E["int update\n(T entity)"]    A --> F["int delete\n(Long id)"]    A --> G["T findOne\n(String sql, Object... params)"]    A --> H["List<T> findList\n(String sql, Object... params)"]        B --> I["👤 UserDao"]    C --> I    D --> I    E --> I    F --> I        style A fill:#e3f2fd    style I fill:#c8e6c9</pre></div><h3 id="8-2-BaseDao-实现"><a href="#8-2-BaseDao-实现" class="headerlink" title="8.2 BaseDao 实现"></a>8.2 BaseDao 实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.util.DruidUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.*;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 泛型 BaseDao：封装通用的增删改查操作</span></span><br><span class="line"><span class="comment"> * 子类只需实现 extractEntity() 方法即可拥有完整的 CRUD 功能</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">BaseDao</span>&lt;T&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">findById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM &quot;</span> + getTableName() + <span class="string">&quot; WHERE id = ?&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            ps.setLong(<span class="number">1</span>, id);</span><br><span class="line">            </span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                <span class="keyword">return</span> extractEntity(rs);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询所有</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;T&gt; <span class="title function_">findAll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM &quot;</span> + getTableName() + <span class="string">&quot; ORDER BY id DESC&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            </span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            List&lt;T&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">                list.add(extractEntity(rs));</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> list;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通用查询单个（带参数）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">findOne</span><span class="params">(String sql, Object... params)</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 设置参数</span></span><br><span class="line">            setParameters(ps, params);</span><br><span class="line">            </span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                <span class="keyword">return</span> extractEntity(rs);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通用查询列表（带参数）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;T&gt; <span class="title function_">findList</span><span class="params">(String sql, Object... params)</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            </span><br><span class="line">            setParameters(ps, params);</span><br><span class="line">            </span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            List&lt;T&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">                list.add(extractEntity(rs));</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> list;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">insert</span><span class="params">(T entity)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> buildInsertSql();</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 设置插入参数（由子类实现）</span></span><br><span class="line">            setInsertParameters(ps, entity);</span><br><span class="line">            </span><br><span class="line">            <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> ps.executeUpdate();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 获取自增主键</span></span><br><span class="line">            <span class="keyword">if</span> (rows &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                rs = ps.getGeneratedKeys();</span><br><span class="line">                <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                    setId(entity, rs.getLong(<span class="number">1</span>));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> rows;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(rs, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 更新数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(T entity)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> buildUpdateSql();</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 设置更新参数</span></span><br><span class="line">            setUpdateParameters(ps, entity);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> ps.executeUpdate();</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 删除</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;DELETE FROM &quot;</span> + getTableName() + <span class="string">&quot; WHERE id = ?&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(sql);</span><br><span class="line">            ps.setLong(<span class="number">1</span>, id);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> ps.executeUpdate();</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            DruidUtil.close(<span class="literal">null</span>, ps, conn);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置预编译 SQL 参数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setParameters</span><span class="params">(PreparedStatement ps, Object... params)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="keyword">if</span> (params == <span class="literal">null</span> || params.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; params.length; i++) &#123;</span><br><span class="line">            <span class="type">Object</span> <span class="variable">param</span> <span class="operator">=</span> params[i];</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (param == <span class="literal">null</span>) &#123;</span><br><span class="line">                ps.setNull(i + <span class="number">1</span>, Types.NULL);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> Integer) &#123;</span><br><span class="line">                ps.setInt(i + <span class="number">1</span>, (Integer) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> Long) &#123;</span><br><span class="line">                ps.setLong(i + <span class="number">1</span>, (Long) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> String) &#123;</span><br><span class="line">                ps.setString(i + <span class="number">1</span>, (String) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> java.util.Date) &#123;</span><br><span class="line">                ps.setTimestamp(i + <span class="number">1</span>, <span class="keyword">new</span> <span class="title class_">Timestamp</span>(((java.util.Date) param).getTime()));</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> java.sql.Timestamp) &#123;</span><br><span class="line">                ps.setTimestamp(i + <span class="number">1</span>, (java.sql.Timestamp) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> BigDecimal) &#123;</span><br><span class="line">                ps.setBigDecimal(i + <span class="number">1</span>, (BigDecimal) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> Double) &#123;</span><br><span class="line">                ps.setDouble(i + <span class="number">1</span>, (Double) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (param <span class="keyword">instanceof</span> Boolean) &#123;</span><br><span class="line">                ps.setBoolean(i + <span class="number">1</span>, (Boolean) param);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                ps.setObject(i + <span class="number">1</span>, param);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==================== 抽象方法：子类实现 ====================</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取表名</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> String <span class="title function_">getTableName</span><span class="params">()</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从 ResultSet 提取实体对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> T <span class="title function_">extractEntity</span><span class="params">(ResultSet rs)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置插入 SQL 的参数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setInsertParameters</span><span class="params">(PreparedStatement ps, T entity)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置更新 SQL 的参数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setUpdateParameters</span><span class="params">(PreparedStatement ps, T entity)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置自增主键到实体对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(T entity, <span class="type">long</span> id)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构建插入 SQL</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> String <span class="title function_">buildInsertSql</span><span class="params">()</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构建更新 SQL</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> String <span class="title function_">buildUpdateSql</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-UserDao-继承-BaseDao"><a href="#8-3-UserDao-继承-BaseDao" class="headerlink" title="8.3 UserDao 继承 BaseDao"></a>8.3 UserDao 继承 BaseDao</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.example.entity.User;</span><br><span class="line"><span class="keyword">import</span> com.example.util.DruidUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"><span class="keyword">import</span> java.sql.PreparedStatement;</span><br><span class="line"><span class="keyword">import</span> java.sql.ResultSet;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLException;</span><br><span class="line"><span class="keyword">import</span> java.sql.Statement;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用户 DAO：继承 BaseDao，只需实现抽象方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDao</span> <span class="keyword">extends</span> <span class="title class_">BaseDao</span>&lt;User&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> String <span class="title function_">getTableName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;users&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> User <span class="title function_">extractEntity</span><span class="params">(ResultSet rs)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">        user.setId(rs.getLong(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">        user.setUsername(rs.getString(<span class="string">&quot;username&quot;</span>));</span><br><span class="line">        user.setPassword(rs.getString(<span class="string">&quot;password&quot;</span>));</span><br><span class="line">        user.setEmail(rs.getString(<span class="string">&quot;email&quot;</span>));</span><br><span class="line">        user.setStatus(rs.getInt(<span class="string">&quot;status&quot;</span>));</span><br><span class="line">        user.setCreatedAt(rs.getTimestamp(<span class="string">&quot;created_at&quot;</span>));</span><br><span class="line">        user.setUpdatedAt(rs.getTimestamp(<span class="string">&quot;updated_at&quot;</span>));</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setInsertParameters</span><span class="params">(PreparedStatement ps, User user)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        ps.setString(<span class="number">1</span>, user.getUsername());</span><br><span class="line">        ps.setString(<span class="number">2</span>, user.getPassword());</span><br><span class="line">        ps.setString(<span class="number">3</span>, user.getEmail());</span><br><span class="line">        ps.setInt(<span class="number">4</span>, user.getStatus() != <span class="literal">null</span> ? user.getStatus() : <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setUpdateParameters</span><span class="params">(PreparedStatement ps, User user)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">        ps.setString(<span class="number">1</span>, user.getUsername());</span><br><span class="line">        ps.setString(<span class="number">2</span>, user.getPassword());</span><br><span class="line">        ps.setString(<span class="number">3</span>, user.getEmail());</span><br><span class="line">        ps.setInt(<span class="number">4</span>, user.getStatus());</span><br><span class="line">        ps.setLong(<span class="number">5</span>, user.getId());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(User user, <span class="type">long</span> id)</span> &#123;</span><br><span class="line">        user.setId(id);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> String <span class="title function_">buildInsertSql</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;INSERT INTO users (username, password, email, status) VALUES (?, ?, ?, ?)&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> String <span class="title function_">buildUpdateSql</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;UPDATE users SET username = ?, password = ?, email = ?, status = ? WHERE id = ?&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ==================== 扩展方法：业务相关查询 ====================</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据用户名和密码查询（登录）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">login</span><span class="params">(String username, String password)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE username = ? AND password = ? AND status = 1&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> findOne(sql, username, password);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> java.util.List&lt;User&gt; <span class="title function_">findByPage</span><span class="params">(<span class="type">int</span> pageNum, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">offset</span> <span class="operator">=</span> (pageNum - <span class="number">1</span>) * pageSize;</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users ORDER BY id ASC LIMIT ?, ?&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> findList(sql, offset, pageSize);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 条件查询：用户名模糊匹配 + 状态筛选</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> java.util.List&lt;User&gt; <span class="title function_">search</span><span class="params">(String keyword, Integer status)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(<span class="string">&quot;SELECT * FROM users WHERE 1=1&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (keyword != <span class="literal">null</span> &amp;&amp; !keyword.trim().isEmpty()) &#123;</span><br><span class="line">            sql.append(<span class="string">&quot; AND username LIKE ?&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (status != <span class="literal">null</span>) &#123;</span><br><span class="line">            sql.append(<span class="string">&quot; AND status = ?&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        sql.append(<span class="string">&quot; ORDER BY created_at DESC&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (keyword != <span class="literal">null</span> &amp;&amp; !keyword.trim().isEmpty() &amp;&amp; status != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> findList(sql.toString(), <span class="string">&quot;%&quot;</span> + keyword + <span class="string">&quot;%&quot;</span>, status);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (keyword != <span class="literal">null</span> &amp;&amp; !keyword.trim().isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> findList(sql.toString(), <span class="string">&quot;%&quot;</span> + keyword + <span class="string">&quot;%&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (status != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> findList(sql.toString(), status);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> findList(sql.toString());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="九、异常处理与最佳实践"><a href="#九、异常处理与最佳实践" class="headerlink" title="九、异常处理与最佳实践"></a>九、异常处理与最佳实践</h2><h3 id="9-1-JDBC-异常处理原则"><a href="#9-1-JDBC-异常处理原则" class="headerlink" title="9.1 JDBC 异常处理原则"></a>9.1 JDBC 异常处理原则</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * JDBC 异常处理最佳实践</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcExceptionHandling</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">properExceptionHandling</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn = DruidUtil.getConnection();</span><br><span class="line">            ps = conn.prepareStatement(<span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>);</span><br><span class="line">            ps.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line">            rs = ps.executeQuery();</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;找到用户：&quot;</span> + rs.getString(<span class="string">&quot;username&quot;</span>));</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="comment">// 分类处理不同类型的 SQL 异常</span></span><br><span class="line">            System.out.println(<span class="string">&quot;❌ SQL 异常：&quot;</span> + e.getMessage());</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 常见异常类型判断</span></span><br><span class="line">            <span class="keyword">if</span> (e <span class="keyword">instanceof</span> java.sql.SQLIntegrityConstraintViolationException) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;数据完整性冲突：如重复的主键或唯一约束&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> java.sql.SQLSyntaxErrorException) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;SQL 语法错误&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> java.sql.SQLTimeoutException) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;SQL 执行超时&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> java.sql.SQLDataException) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;数据异常：如数据类型不匹配&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> com.mysql.cj.jdbc.exceptions.CommunicationsException) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;数据库连接通信异常&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 必须在 finally 中关闭资源</span></span><br><span class="line">            <span class="keyword">if</span> (rs != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    rs.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (ps != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    ps.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (conn != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    conn.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-2-资源关闭的正确方式"><a href="#9-2-资源关闭的正确方式" class="headerlink" title="9.2 资源关闭的正确方式"></a>9.2 资源关闭的正确方式</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用 try-with-resources 自动关闭资源（推荐）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">tryWithResourcesExample</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// Java 7+ 支持 try-with-resources，资源会自动关闭</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> (</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> DruidUtil.getConnection();</span><br><span class="line">        <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> conn.prepareStatement(sql);</span><br><span class="line">        <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> ps.executeQuery();</span><br><span class="line">    ) &#123;</span><br><span class="line">        ps.setLong(<span class="number">1</span>, <span class="number">1L</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">while</span> (rs.next()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;用户名：&quot;</span> + rs.getString(<span class="string">&quot;username&quot;</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 无需手动关闭，资源自动关闭</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 封装统一的关闭方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(AutoCloseable... resources)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (AutoCloseable resource : resources) &#123;</span><br><span class="line">        <span class="keyword">if</span> (resource != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                resource.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-3-JDBC-最佳实践清单"><a href="#9-3-JDBC-最佳实践清单" class="headerlink" title="9.3 JDBC 最佳实践清单"></a>9.3 JDBC 最佳实践清单</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["✅ JDBC 最佳实践"] --> B["连接管理"]    A --> C["SQL 编写"]    A --> D["事务处理"]    A --> E["性能优化"]    A --> F["安全防护"]        B --> B1["使用连接池\n不用传统 JDBC"]    B --> B2["使用 ThreadLocal\n保证线程安全"]    B --> B3["正确关闭资源\nfinally 中关闭"]        C --> C1["使用 PreparedStatement\n防止 SQL 注入"]    C --> C2["避免 SELECT *\n明确指定列名"]    C --> C3["合理使用 LIMIT\n避免全表扫描"]        D --> D1["事务不宜过长\n减少锁竞争"]    D --> D2["手动提交\nsetAutoCommit(false)"]    D --> D3["异常必须回滚\nrollback()"]        E --> E1["批量操作\naddBatch()"]    E --> E2["使用索引\nEXPLAIN 分析"]    E --> E3["连接池调优\n合理配置参数"]        F --> F1["预编译 SQL\n参数绑定"]    F --> F2["密码加密\nBCrypt 等"]    F --> F3["输入校验\n白名单过滤"]        style A fill:#e3f2fd</pre></div><hr><h2 id="十、总结"><a href="#十、总结" class="headerlink" title="十、总结"></a>十、总结</h2><h3 id="10-1-核心知识点回顾"><a href="#10-1-核心知识点回顾" class="headerlink" title="10.1 核心知识点回顾"></a>10.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((JDBC 核心知识))    基础概念      JDBC 架构      驱动类型      URL 连接字符串    核心 API      DriverManager      Connection      Statement      PreparedStatement      ResultSet    CRUD 操作      查询 executeQuery      插入 executeUpdate      更新 executeUpdate      删除 executeUpdate    事务管理      自动提交      手动提交 commit      回滚 rollback      保存点    SQL 注入防护      PreparedStatement      参数绑定      预编译机制    连接池      Druid      HikariCP      原理：复用连接    性能优化      批量操作      分页查询      索引优化    异常处理      SQLException      资源关闭      事务回滚</pre></div><h3 id="10-2-学习路线建议"><a href="#10-2-学习路线建议" class="headerlink" title="10.2 学习路线建议"></a>10.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["第一阶段\n基础入门"] --> B["第二阶段\n核心API"]    B --> C["第三阶段\n高级特性"]    C --> D["第四阶段\n实战封装"]        A --> A1["JDBC 概念"]    A --> A2["驱动加载"]    A --> A3["简单 CRUD"]        B --> B1["Connection"]    B --> B2["PreparedStatement"]    B --> B3["ResultSet"]        C --> C1["事务管理"]    C --> C2["连接池"]    C --> C3["性能优化"]        D --> D1["BaseDao 封装"]    D --> D2["ORM 框架\n（如 MyBatis）"]    D --> D3["项目实战"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0</pre></div><h3 id="10-3-下一步推荐学习"><a href="#10-3-下一步推荐学习" class="headerlink" title="10.3 下一步推荐学习"></a>10.3 下一步推荐学习</h3><ul><li>📖 <strong>MyBatis</strong>：更强大的持久层框架，简化 JDBC 操作</li><li>📖 <strong>Hibernate</strong>：全自动 ORM 框架</li><li>📖 <strong>Spring JdbcTemplate</strong>：Spring 提供的 JDBC 封装</li><li>📖 <strong>《高性能 MySQL》</strong>：深入理解数据库原理</li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：JDBC 是 Java 数据库操作的根基，虽然现在有很多 ORM 框架简化了数据库操作，但理解 JDBC 的原理能让你在遇到问题时快速定位根源。”知其然，更知其所以然”，共勉！</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/jdbc-complete-guide/</id>
    <link href="https://blog.codenav.top/jdbc-complete-guide/"/>
    <published>2026-05-24T05:30:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="JDBC-零基础入门到实战：手把手教你用-Java-操作数据库-☕️"><a href="#JDBC-零基础入门到实战：手把手教你用-Java-操作数据库-☕️" class="headerlink" title="JDBC 零基础入门到实战：手把手教你用 Jav]]>
    </summary>
    <title>JDBC 零基础入门到实战：手把手教你用 Java 操作数据库 ☕️</title>
    <updated>2026-05-24T05:31:39.769Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="数据库" scheme="https://blog.codenav.top/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <category term="后端" scheme="https://blog.codenav.top/tags/%E5%90%8E%E7%AB%AF/"/>
    <category term="MySQL" scheme="https://blog.codenav.top/tags/MySQL/"/>
    <category term="数据库" scheme="https://blog.codenav.top/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <category term="SQL" scheme="https://blog.codenav.top/tags/SQL/"/>
    <content>
      <![CDATA[<h1 id="MySQL-数据库从入门到精通：核心知识点全覆盖-🗄️"><a href="#MySQL-数据库从入门到精通：核心知识点全覆盖-🗄️" class="headerlink" title="MySQL 数据库从入门到精通：核心知识点全覆盖 🗄️"></a>MySQL 数据库从入门到精通：核心知识点全覆盖 🗄️</h1><blockquote><p>MySQL 是世界上最受欢迎的开源关系型数据库之一，凭借其高性能、高可靠性和易用性，广泛应用于各类 Web 应用和企业级系统。本文将带你系统掌握 MySQL 的核心知识点，从基础操作到性能优化，图文并茂，干货满满！💪</p></blockquote><hr><h2 id="📚-目录导航"><a href="#📚-目录导航" class="headerlink" title="📚 目录导航"></a>📚 目录导航</h2><ul><li><a href="#%E4%B8%80mysql-%E6%A6%82%E8%BF%B0%E4%B8%8E%E6%9E%B6%E6%9E%84">一、MySQL 概述与架构</a></li><li><a href="#%E4%BA%8C%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E8%AF%A6%E8%A7%A3">二、数据类型详解</a></li><li><a href="#%E4%B8%89%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E%E8%A1%A8%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C">三、数据库与表的基本操作</a></li><li><a href="#%E5%9B%9Bcrud-%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5">四、CRUD 增删改查</a></li><li><a href="#%E4%BA%94%E7%B4%A2%E5%BC%95%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E6%80%A7%E8%83%BD%E4%B9%8B%E9%92%A5">五、索引：数据库的性能之钥</a></li><li><a href="#%E5%85%AD%E4%BA%8B%E5%8A%A1%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7%E7%9A%84%E5%AE%88%E6%8A%A4%E8%80%85">六、事务：数据一致性的守护者</a></li><li><a href="#%E4%B8%83%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E4%B8%8E%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92">七、查询优化与执行计划</a></li><li><a href="#%E5%85%AB%E5%A4%87%E4%BB%BD%E4%B8%8E%E6%81%A2%E5%A4%8D">八、备份与恢复</a></li><li><a href="#%E4%B9%9D%E6%80%BB%E7%BB%93%E4%B8%8E%E7%9F%A5%E8%AF%86%E6%A1%86%E6%9E%B6">九、总结与知识框架</a></li></ul><hr><h2 id="一、MySQL-概述与架构"><a href="#一、MySQL-概述与架构" class="headerlink" title="一、MySQL 概述与架构"></a>一、MySQL 概述与架构</h2><h3 id="1-1-什么是-MySQL？"><a href="#1-1-什么是-MySQL？" class="headerlink" title="1.1 什么是 MySQL？"></a>1.1 什么是 MySQL？</h3><p>MySQL 是一款<strong>开源的关系型数据库管理系统</strong>（RDBMS），由瑞典 MySQL AB 公司开发，现属于 Oracle 旗下。它使用 <strong>SQL（Structured Query Language）</strong> 作为操作语言，支持多线程、多用户，能够处理大量数据。</p><h3 id="1-2-MySQL-的整体架构"><a href="#1-2-MySQL-的整体架构" class="headerlink" title="1.2 MySQL 的整体架构"></a>1.2 MySQL 的整体架构</h3><p>MySQL 采用<strong>插件式存储引擎架构</strong>，这种设计让它在灵活性上表现出色。我们可以通过一张架构图来理解 MySQL 的工作层次：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    subgraph 应用层["📱 应用层"]        A["连接器 Connection Pool"]        B["SQL 接口 SQL Interface"]        C["解析器 Parser"]        D["优化器 Optimizer"]        E["缓存与缓冲 Cache & Buffer"]    end        subgraph 引擎层["⚙️ 存储引擎层（插件式）"]        F["InnoDB（默认）"]        G["MyISAM"]        H["Memory"]        I["Archive"]    end        subgraph 物理层["💾 物理存储层"]        J["数据文件 Data Files"]        K["索引文件 Index Files"]        L["日志文件 Log Files"]    end        A --> B --> C --> D --> E    D --> F    D --> G    D --> H    D --> I    F --> J    G --> J    H --> J    I --> J    F --> K    G --> K    F --> L        style A fill:#e3f2fd    style F fill:#c8e6c9    style J fill:#fff3e0</pre></div><h3 id="1-3-核心存储引擎对比"><a href="#1-3-核心存储引擎对比" class="headerlink" title="1.3 核心存储引擎对比"></a>1.3 核心存储引擎对比</h3><p>MySQL 支持多种存储引擎，不同引擎在功能特性上差异明显：</p><table><thead><tr><th>特性</th><th>InnoDB</th><th>MyISAM</th><th>Memory</th></tr></thead><tbody><tr><td><strong>事务支持</strong></td><td>✅ 支持</td><td>❌ 不支持</td><td>❌ 不支持</td></tr><tr><td><strong>行级锁</strong></td><td>✅ 支持</td><td>❌ 表级锁</td><td>❌ 表级锁</td></tr><tr><td><strong>外键约束</strong></td><td>✅ 支持</td><td>❌ 不支持</td><td>❌ 不支持</td></tr><tr><td><strong>崩溃恢复</strong></td><td>✅ 支持</td><td>❌ 不支持</td><td>❌ 不支持</td></tr><tr><td><strong>全文索引</strong></td><td>✅ 支持（5.6+）</td><td>✅ 支持</td><td>❌ 不支持</td></tr><tr><td><strong>存储限制</strong></td><td>64TB</td><td>256TB</td><td>受 RAM 限制</td></tr><tr><td><strong>适用场景</strong></td><td>事务业务、高并发</td><td>只读&#x2F;静态表</td><td>临时表、缓存</td></tr></tbody></table><blockquote><p>💡 <strong>推荐</strong>：日常业务开发首选 <strong>InnoDB</strong>，它是 MySQL 5.5 之后的默认引擎，支持事务和行锁，功能全面。</p></blockquote><h3 id="1-4-连接-MySQL-的几种方式"><a href="#1-4-连接-MySQL-的几种方式" class="headerlink" title="1.4 连接 MySQL 的几种方式"></a>1.4 连接 MySQL 的几种方式</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方式一：命令行客户端连接</span></span><br><span class="line">mysql -u root -p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二：指定主机和端口</span></span><br><span class="line">mysql -h 192.168.1.100 -P 3306 -u root -p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式三：连接后选择数据库</span></span><br><span class="line">mysql -u root -p</span><br><span class="line">USE my_database;</span><br><span class="line">SHOW TABLES;</span><br></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 查看 MySQL 版本和状态</span></span><br><span class="line"><span class="keyword">SELECT</span> VERSION();</span><br><span class="line"><span class="keyword">SHOW</span> STATUS;</span><br><span class="line"><span class="keyword">SHOW</span> VARIABLES <span class="keyword">LIKE</span> <span class="string">&#x27;version%&#x27;</span>;</span><br></pre></td></tr></table></figure><hr><h2 id="二、数据类型详解"><a href="#二、数据类型详解" class="headerlink" title="二、数据类型详解"></a>二、数据类型详解</h2><p>MySQL 支持多种数据类型，合理选择数据类型不仅能节省存储空间，还能提升查询性能。</p><h3 id="2-1-数值类型"><a href="#2-1-数值类型" class="headerlink" title="2.1 数值类型"></a>2.1 数值类型</h3><table><thead><tr><th>类型</th><th>大小</th><th>范围</th><th>用途</th></tr></thead><tbody><tr><td><strong>TINYINT</strong></td><td>1 字节</td><td>-128 ~ 127 或 0 ~ 255</td><td>小整数，如年龄、状态码</td></tr><tr><td><strong>SMALLINT</strong></td><td>2 字节</td><td>-32768 ~ 32767</td><td>中等整数</td></tr><tr><td><strong>INT &#x2F; INTEGER</strong></td><td>4 字节</td><td>-21亿 ~ 21亿</td><td>常规整数，<strong>最常用</strong></td></tr><tr><td><strong>BIGINT</strong></td><td>8 字节</td><td>极大范围</td><td>大整数，如 ID</td></tr><tr><td><strong>FLOAT</strong></td><td>4 字节</td><td>浮点数</td><td>单精度</td></tr><tr><td><strong>DOUBLE</strong></td><td>8 字节</td><td>浮点数</td><td>双精度，<strong>常用</strong></td></tr><tr><td><strong>DECIMAL(M,D)</strong></td><td>变长</td><td>精确小数</td><td>金额、精度要求高的数据</td></tr></tbody></table><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 示例：创建用户表，展示数值类型的使用</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> <span class="keyword">user</span> (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;用户ID&#x27;</span>,</span><br><span class="line">    age TINYINT UNSIGNED COMMENT <span class="string">&#x27;年龄（0-255）&#x27;</span>,</span><br><span class="line">    balance <span class="type">DECIMAL</span>(<span class="number">10</span>, <span class="number">2</span>) COMMENT <span class="string">&#x27;账户余额（精确到分）&#x27;</span>,</span><br><span class="line">    score <span class="keyword">DOUBLE</span> COMMENT <span class="string">&#x27;评分（可有小数）&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4;</span><br></pre></td></tr></table></figure><h3 id="2-2-字符串类型"><a href="#2-2-字符串类型" class="headerlink" title="2.2 字符串类型"></a>2.2 字符串类型</h3><table><thead><tr><th>类型</th><th>最大长度</th><th>特点</th></tr></thead><tbody><tr><td><strong>CHAR(N)</strong></td><td>255 字符</td><td>固定长度，不足补空格；适合定长内容如手机号</td></tr><tr><td><strong>VARCHAR(N)</strong></td><td>16383 字符</td><td>变长，<strong>最常用</strong>；节省空间</td></tr><tr><td><strong>TEXT</strong></td><td>65535 字节</td><td>长文本，如文章内容</td></tr><tr><td><strong>MEDIUMTEXT</strong></td><td>16MB</td><td>中等长文本</td></tr><tr><td><strong>LONGTEXT</strong></td><td>4GB</td><td>超长文本</td></tr></tbody></table><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 示例：创建文章表，展示字符串类型</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> article (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    title <span class="type">VARCHAR</span>(<span class="number">200</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;文章标题&#x27;</span>,</span><br><span class="line">    content TEXT COMMENT <span class="string">&#x27;文章内容&#x27;</span>,</span><br><span class="line">    author <span class="type">CHAR</span>(<span class="number">18</span>) COMMENT <span class="string">&#x27;作者身份证号（定长）&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- VARCHAR vs CHAR 性能对比</span></span><br><span class="line"><span class="comment">-- VARCHAR：存储变长，节省空间，但写入时可能需要额外操作</span></span><br><span class="line"><span class="comment">-- CHAR：固定长度，查询快，但浪费空间；适合长度固定的场景</span></span><br></pre></td></tr></table></figure><blockquote><p>💡 <strong>建议</strong>：对于中文内容，一定要使用 <code>utf8mb4</code> 字符集，一个中文占 4 字节，<code>utf8</code> 只支持 3 字节可能导致表情符号存储失败。</p></blockquote><h3 id="2-3-日期时间类型"><a href="#2-3-日期时间类型" class="headerlink" title="2.3 日期时间类型"></a>2.3 日期时间类型</h3><table><thead><tr><th>类型</th><th>格式</th><th>范围</th></tr></thead><tbody><tr><td><strong>DATE</strong></td><td>YYYY-MM-DD</td><td>1000-01-01 ~ 9999-12-31</td></tr><tr><td><strong>TIME</strong></td><td>HH:MM:SS</td><td>-838:59:59 ~ 838:59:59</td></tr><tr><td><strong>DATETIME</strong></td><td>YYYY-MM-DD HH:MM:SS</td><td>1000-01-01 ~ 9999-12-31 23:59:59</td></tr><tr><td><strong>TIMESTAMP</strong></td><td>YYYY-MM-DD HH:MM:SS</td><td>1970-01-01 00:00:01 ~ 2038-01-19</td></tr><tr><td><strong>YEAR</strong></td><td>YYYY</td><td>1901 ~ 2155</td></tr></tbody></table><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 示例：订单表，展示日期时间类型</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> orders (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    order_no <span class="type">VARCHAR</span>(<span class="number">32</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;订单号&#x27;</span>,</span><br><span class="line">    create_time DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;下单时间&#x27;</span>,</span><br><span class="line">    update_time <span class="type">TIMESTAMP</span> <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">    pay_time <span class="type">DATE</span> COMMENT <span class="string">&#x27;支付日期&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- TIMESTAMP vs DATETIME 的关键区别：</span></span><br><span class="line"><span class="comment">-- 1. TIMESTAMP 占用 4 字节，DATETIME 占用 8 字节</span></span><br><span class="line"><span class="comment">-- 2. TIMESTAMP 会自动转换时区，DATETIME 不会</span></span><br><span class="line"><span class="comment">-- 3. TIMESTAMP 范围有限（到 2038 年），DATETIME 范围更大</span></span><br></pre></td></tr></table></figure><h3 id="2-4-枚举与集合类型"><a href="#2-4-枚举与集合类型" class="headerlink" title="2.4 枚举与集合类型"></a>2.4 枚举与集合类型</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- ENUM：单选，从预定义列表中选择一个值</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> product (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    name <span class="type">VARCHAR</span>(<span class="number">100</span>),</span><br><span class="line">    size ENUM(<span class="string">&#x27;S&#x27;</span>, <span class="string">&#x27;M&#x27;</span>, <span class="string">&#x27;L&#x27;</span>, <span class="string">&#x27;XL&#x27;</span>) COMMENT <span class="string">&#x27;尺码&#x27;</span>,</span><br><span class="line">    color ENUM(<span class="string">&#x27;red&#x27;</span>, <span class="string">&#x27;green&#x27;</span>, <span class="string">&#x27;blue&#x27;</span>, <span class="string">&#x27;black&#x27;</span>, <span class="string">&#x27;white&#x27;</span>) COMMENT <span class="string">&#x27;颜色&#x27;</span>,</span><br><span class="line">    status ENUM(<span class="string">&#x27;pending&#x27;</span>, <span class="string">&#x27;processing&#x27;</span>, <span class="string">&#x27;shipped&#x27;</span>, <span class="string">&#x27;delivered&#x27;</span>) <span class="keyword">DEFAULT</span> <span class="string">&#x27;pending&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- SET：多选，从预定义列表中选择多个值</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> user_hobby (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    user_name <span class="type">VARCHAR</span>(<span class="number">50</span>),</span><br><span class="line">    hobbies <span class="keyword">SET</span>(<span class="string">&#x27;reading&#x27;</span>, <span class="string">&#x27;sports&#x27;</span>, <span class="string">&#x27;music&#x27;</span>, <span class="string">&#x27;travel&#x27;</span>, <span class="string">&#x27;coding&#x27;</span>) COMMENT <span class="string">&#x27;爱好（可多选）&#x27;</span></span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB;</span><br></pre></td></tr></table></figure><hr><h2 id="三、数据库与表的基本操作"><a href="#三、数据库与表的基本操作" class="headerlink" title="三、数据库与表的基本操作"></a>三、数据库与表的基本操作</h2><h3 id="3-1-数据库操作"><a href="#3-1-数据库操作" class="headerlink" title="3.1 数据库操作"></a>3.1 数据库操作</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建数据库</span></span><br><span class="line"><span class="keyword">CREATE</span> DATABASE IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> my_blog <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8mb4_unicode_ci;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查看所有数据库</span></span><br><span class="line"><span class="keyword">SHOW</span> DATABASES;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 选择数据库</span></span><br><span class="line">USE my_blog;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查看当前数据库</span></span><br><span class="line"><span class="keyword">SELECT</span> DATABASE();</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 删除数据库（谨慎操作！）</span></span><br><span class="line"><span class="keyword">DROP</span> DATABASE IF <span class="keyword">EXISTS</span> my_blog;</span><br></pre></td></tr></table></figure><h3 id="3-2-表的创建与修改"><a href="#3-2-表的创建与修改" class="headerlink" title="3.2 表的创建与修改"></a>3.2 表的创建与修改</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建一个完整的用户表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> users (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;用户ID&#x27;</span>,</span><br><span class="line">    username <span class="type">VARCHAR</span>(<span class="number">50</span>) <span class="keyword">NOT NULL</span> <span class="keyword">UNIQUE</span> COMMENT <span class="string">&#x27;用户名&#x27;</span>,</span><br><span class="line">    email <span class="type">VARCHAR</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;邮箱&#x27;</span>,</span><br><span class="line">    password <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;密码（加密存储）&#x27;</span>,</span><br><span class="line">    phone <span class="type">CHAR</span>(<span class="number">11</span>) COMMENT <span class="string">&#x27;手机号&#x27;</span>,</span><br><span class="line">    avatar <span class="type">VARCHAR</span>(<span class="number">500</span>) <span class="keyword">DEFAULT</span> <span class="string">&#x27;/static/img/default.png&#x27;</span> COMMENT <span class="string">&#x27;头像URL&#x27;</span>,</span><br><span class="line">    status TINYINT <span class="keyword">DEFAULT</span> <span class="number">1</span> COMMENT <span class="string">&#x27;状态：1-正常，0-禁用&#x27;</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">    updated_at <span class="type">TIMESTAMP</span> <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="built_in">CURRENT_TIMESTAMP</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="comment">-- 添加索引</span></span><br><span class="line">    INDEX idx_email (email),</span><br><span class="line">    INDEX idx_status (status)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4 COMMENT<span class="operator">=</span><span class="string">&#x27;用户表&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查看表结构</span></span><br><span class="line"><span class="keyword">DESC</span> users;</span><br><span class="line"><span class="keyword">DESCRIBE</span> users;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查看建表语句（完整信息）</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">CREATE TABLE</span> users;</span><br></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 修改表：添加新字段</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> users <span class="keyword">ADD</span> <span class="keyword">COLUMN</span> last_login_time DATETIME COMMENT <span class="string">&#x27;最后登录时间&#x27;</span> AFTER created_at;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 修改表：修改字段</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> users MODIFY <span class="keyword">COLUMN</span> phone <span class="type">VARCHAR</span>(<span class="number">20</span>) COMMENT <span class="string">&#x27;手机号（可变长）&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 修改表：删除字段</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> users <span class="keyword">DROP</span> <span class="keyword">COLUMN</span> avatar;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 修改表：添加索引</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> users <span class="keyword">ADD</span> INDEX idx_phone (phone);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 修改表：重命名表</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> users RENAME <span class="keyword">TO</span> user_info;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 删除表（谨慎操作！）</span></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> users;</span><br></pre></td></tr></table></figure><h3 id="3-3-约束详解"><a href="#3-3-约束详解" class="headerlink" title="3.3 约束详解"></a>3.3 约束详解</h3><p>约束是用来限制字段取值合法性的规则，是保证数据完整性的重要手段：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["数据完整性约束"] --> B["主键约束\nPrimary Key"]    A --> C["唯一约束\nUnique Key"]    A --> D["非空约束\nNot Null"]    A --> E["检查约束\nCheck"]    A --> F["外键约束\nForeign Key"]    A --> G["默认值约束\nDefault"]        B --> B1["唯一标识\n自动索引"]    C --> C1["字段值不可重复"]    D --> D1["字段必须有值"]    E --> E1["字段值满足条件"]    F --> F1["表间关联关系"]    G --> G1["未填时自动填入"]        style B fill:#c8e6c9    style C fill:#c8e6c9    style D fill:#e3f2fd    style E fill:#fff3e0    style F fill:#f8bbd0    style G fill:#f3e5f5</pre></div><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建带完整约束的订单表</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> orders (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    order_no <span class="type">VARCHAR</span>(<span class="number">64</span>) <span class="keyword">NOT NULL</span> <span class="keyword">UNIQUE</span> COMMENT <span class="string">&#x27;订单号&#x27;</span>,</span><br><span class="line">    user_id <span class="type">BIGINT</span> <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;用户ID&#x27;</span>,</span><br><span class="line">    total_amount <span class="type">DECIMAL</span>(<span class="number">10</span>, <span class="number">2</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="number">0.00</span> COMMENT <span class="string">&#x27;订单总额&#x27;</span>,</span><br><span class="line">    status ENUM(<span class="string">&#x27;pending&#x27;</span>, <span class="string">&#x27;paid&#x27;</span>, <span class="string">&#x27;shipped&#x27;</span>, <span class="string">&#x27;completed&#x27;</span>, <span class="string">&#x27;cancelled&#x27;</span>) <span class="keyword">DEFAULT</span> <span class="string">&#x27;pending&#x27;</span>,</span><br><span class="line">    created_at DATETIME <span class="keyword">DEFAULT</span> <span class="built_in">CURRENT_TIMESTAMP</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="comment">-- 外键约束：关联用户表</span></span><br><span class="line">    <span class="keyword">FOREIGN KEY</span> (user_id) <span class="keyword">REFERENCES</span> users(id) </span><br><span class="line">        <span class="keyword">ON</span> <span class="keyword">DELETE</span> RESTRICT </span><br><span class="line">        <span class="keyword">ON</span> <span class="keyword">UPDATE</span> CASCADE,</span><br><span class="line">    </span><br><span class="line">    <span class="comment">-- 检查约束：金额必须大于等于 0（MySQL 8.0.16+）</span></span><br><span class="line">    <span class="keyword">CHECK</span> (total_amount <span class="operator">&gt;=</span> <span class="number">0</span>)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8mb4;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 约束相关的查看与删除</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">CREATE TABLE</span> orders;</span><br><span class="line"><span class="keyword">ALTER TABLE</span> orders <span class="keyword">DROP</span> <span class="keyword">FOREIGN KEY</span> orders_ibfk_1;</span><br><span class="line"><span class="keyword">ALTER TABLE</span> orders <span class="keyword">DROP</span> <span class="keyword">CHECK</span> orders_chk_1;</span><br></pre></td></tr></table></figure><hr><h2 id="四、CRUD-增删改查"><a href="#四、CRUD-增删改查" class="headerlink" title="四、CRUD 增删改查"></a>四、CRUD 增删改查</h2><h3 id="4-1-插入数据（INSERT）"><a href="#4-1-插入数据（INSERT）" class="headerlink" title="4.1 插入数据（INSERT）"></a>4.1 插入数据（INSERT）</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 方式一：插入单条数据</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users (username, email, password, phone) </span><br><span class="line"><span class="keyword">VALUES</span> (<span class="string">&#x27;zhangsan&#x27;</span>, <span class="string">&#x27;zhangsan@example.com&#x27;</span>, <span class="string">&#x27;hashed_password&#x27;</span>, <span class="string">&#x27;13800138000&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 方式二：插入多条数据（效率更高）</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users (username, email, password, phone) <span class="keyword">VALUES</span></span><br><span class="line">(<span class="string">&#x27;lisi&#x27;</span>, <span class="string">&#x27;lisi@example.com&#x27;</span>, <span class="string">&#x27;pass123&#x27;</span>, <span class="string">&#x27;13800138001&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;wangwu&#x27;</span>, <span class="string">&#x27;wangwu@example.com&#x27;</span>, <span class="string">&#x27;pass456&#x27;</span>, <span class="string">&#x27;13800138002&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;zhaoliu&#x27;</span>, <span class="string">&#x27;zhaoliu@example.com&#x27;</span>, <span class="string">&#x27;pass789&#x27;</span>, <span class="string">&#x27;13800138003&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 方式三：直接插入完整数据（省略字段名）</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users <span class="keyword">VALUES</span> (<span class="keyword">NULL</span>, <span class="string">&#x27;tianqi&#x27;</span>, <span class="string">&#x27;tianqi@example.com&#x27;</span>, <span class="string">&#x27;pass000&#x27;</span>, <span class="string">&#x27;13800138004&#x27;</span>, <span class="string">&#x27;/avatar.png&#x27;</span>, <span class="number">1</span>, NOW(), NOW());</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 方式四：插入或更新（主键/唯一冲突时更新）</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users (id, username, email, password) </span><br><span class="line"><span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="string">&#x27;updated_user&#x27;</span>, <span class="string">&#x27;new@example.com&#x27;</span>, <span class="string">&#x27;new_pass&#x27;</span>)</span><br><span class="line"><span class="keyword">ON</span> DUPLICATE KEY <span class="keyword">UPDATE</span> </span><br><span class="line">    username <span class="operator">=</span> <span class="keyword">VALUES</span>(username),</span><br><span class="line">    email <span class="operator">=</span> <span class="keyword">VALUES</span>(email),</span><br><span class="line">    password <span class="operator">=</span> <span class="keyword">VALUES</span>(password);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 方式五：蠕虫复制（从已有表复制数据）</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> users_backup <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> created_at <span class="operator">&gt;</span> <span class="string">&#x27;2026-01-01&#x27;</span>;</span><br></pre></td></tr></table></figure><h3 id="4-2-查询数据（SELECT）"><a href="#4-2-查询数据（SELECT）" class="headerlink" title="4.2 查询数据（SELECT）"></a>4.2 查询数据（SELECT）</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基本查询</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 条件查询</span></span><br><span class="line"><span class="keyword">SELECT</span> username, email <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="number">1</span> <span class="keyword">AND</span> id <span class="operator">&gt;</span> <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 排序查询</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">ORDER</span> <span class="keyword">BY</span> created_at <span class="keyword">DESC</span>, id <span class="keyword">ASC</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 分页查询（重要！）</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">ORDER</span> <span class="keyword">BY</span> id LIMIT <span class="number">10</span> <span class="keyword">OFFSET</span> <span class="number">0</span>;  <span class="comment">-- 第一页，每页10条</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">ORDER</span> <span class="keyword">BY</span> id LIMIT <span class="number">10</span> <span class="keyword">OFFSET</span> <span class="number">10</span>; <span class="comment">-- 第二页</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 简化写法：LIMIT start, count</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">ORDER</span> <span class="keyword">BY</span> id LIMIT <span class="number">0</span>, <span class="number">10</span>;</span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">ORDER</span> <span class="keyword">BY</span> id LIMIT <span class="number">10</span>, <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 聚合查询</span></span><br><span class="line"><span class="keyword">SELECT</span> </span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">AS</span> total_users,</span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="keyword">DISTINCT</span> status) <span class="keyword">AS</span> status_types,</span><br><span class="line">    <span class="built_in">MAX</span>(created_at) <span class="keyword">AS</span> latest_registration,</span><br><span class="line">    <span class="built_in">MIN</span>(created_at) <span class="keyword">AS</span> earliest_registration,</span><br><span class="line">    <span class="built_in">AVG</span>(id) <span class="keyword">AS</span> avg_id</span><br><span class="line"><span class="keyword">FROM</span> users;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 分组查询 + 聚合函数</span></span><br><span class="line"><span class="keyword">SELECT</span> </span><br><span class="line">    status,</span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">AS</span> user_count,</span><br><span class="line">    <span class="built_in">MAX</span>(created_at) <span class="keyword">AS</span> last_time</span><br><span class="line"><span class="keyword">FROM</span> users </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> status </span><br><span class="line"><span class="keyword">HAVING</span> user_count <span class="operator">&gt;</span> <span class="number">5</span>;  <span class="comment">-- HAVING 对分组后的结果进行过滤</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 子查询</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users </span><br><span class="line"><span class="keyword">WHERE</span> id <span class="keyword">IN</span> (<span class="keyword">SELECT</span> user_id <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> total_amount <span class="operator">&gt;</span> <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 关联查询：多表联合</span></span><br><span class="line"><span class="keyword">SELECT</span> </span><br><span class="line">    u.username,</span><br><span class="line">    o.order_no,</span><br><span class="line">    o.total_amount</span><br><span class="line"><span class="keyword">FROM</span> users u</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> orders o <span class="keyword">ON</span> u.id <span class="operator">=</span> o.user_id</span><br><span class="line"><span class="keyword">WHERE</span> o.status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span></span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> o.created_at <span class="keyword">DESC</span>;</span><br></pre></td></tr></table></figure><h3 id="4-3-更新数据（UPDATE）"><a href="#4-3-更新数据（UPDATE）" class="headerlink" title="4.3 更新数据（UPDATE）"></a>4.3 更新数据（UPDATE）</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 更新单个字段</span></span><br><span class="line"><span class="keyword">UPDATE</span> users <span class="keyword">SET</span> phone <span class="operator">=</span> <span class="string">&#x27;13900139000&#x27;</span> <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 更新多个字段</span></span><br><span class="line"><span class="keyword">UPDATE</span> users </span><br><span class="line"><span class="keyword">SET</span> </span><br><span class="line">    email <span class="operator">=</span> <span class="string">&#x27;new_email@example.com&#x27;</span>,</span><br><span class="line">    status <span class="operator">=</span> <span class="number">0</span>,</span><br><span class="line">    updated_at <span class="operator">=</span> NOW()</span><br><span class="line"><span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 批量更新（谨慎使用，最好加条件）</span></span><br><span class="line"><span class="keyword">UPDATE</span> users <span class="keyword">SET</span> status <span class="operator">=</span> <span class="number">1</span> <span class="keyword">WHERE</span> created_at <span class="operator">&gt;</span> <span class="string">&#x27;2026-01-01&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 使用 CASE 进行条件更新</span></span><br><span class="line"><span class="keyword">UPDATE</span> orders </span><br><span class="line"><span class="keyword">SET</span> </span><br><span class="line">    status <span class="operator">=</span> <span class="keyword">CASE</span> </span><br><span class="line">        <span class="keyword">WHEN</span> status <span class="operator">=</span> <span class="string">&#x27;pending&#x27;</span> <span class="keyword">AND</span> pay_time <span class="operator">&lt;</span> DATE_SUB(NOW(), <span class="type">INTERVAL</span> <span class="number">1</span> <span class="keyword">HOUR</span>) <span class="keyword">THEN</span> <span class="string">&#x27;cancelled&#x27;</span></span><br><span class="line">        <span class="keyword">WHEN</span> status <span class="operator">=</span> <span class="string">&#x27;shipped&#x27;</span> <span class="keyword">AND</span> delivery_time <span class="operator">&lt;</span> DATE_SUB(NOW(), <span class="type">INTERVAL</span> <span class="number">7</span> <span class="keyword">DAY</span>) <span class="keyword">THEN</span> <span class="string">&#x27;completed&#x27;</span></span><br><span class="line">        <span class="keyword">ELSE</span> status</span><br><span class="line">    <span class="keyword">END</span></span><br><span class="line"><span class="keyword">WHERE</span> status <span class="keyword">IN</span> (<span class="string">&#x27;pending&#x27;</span>, <span class="string">&#x27;shipped&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="4-4-删除数据（DELETE）"><a href="#4-4-删除数据（DELETE）" class="headerlink" title="4.4 删除数据（DELETE）"></a>4.4 删除数据（DELETE）</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 删除指定数据</span></span><br><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 删除前N条（配合排序使用）</span></span><br><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> users <span class="keyword">ORDER</span> <span class="keyword">BY</span> created_at <span class="keyword">ASC</span> LIMIT <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 截断表（删除全部数据，速度极快，但无法恢复）</span></span><br><span class="line"><span class="keyword">TRUNCATE</span> <span class="keyword">TABLE</span> users;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 清空用户表并重置自增ID</span></span><br><span class="line"><span class="keyword">TRUNCATE</span> <span class="keyword">TABLE</span> users;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- DELETE vs TRUNCATE 区别</span></span><br><span class="line"><span class="comment">-- 1. DELETE 可加 WHERE 条件，TRUNCATE 不行</span></span><br><span class="line"><span class="comment">-- 2. DELETE 记录删除日志（一行一行删除），TRUNCATE 不记录</span></span><br><span class="line"><span class="comment">-- 3. DELETE 不会重置自增ID，TRUNCATE 会</span></span><br><span class="line"><span class="comment">-- 4. DELETE 可触发触发器，TRUNCATE 不会</span></span><br><span class="line"><span class="comment">-- 5. TRUNCATE 更快，DELETE 更安全</span></span><br></pre></td></tr></table></figure><h3 id="4-5-关联查询详解"><a href="#4-5-关联查询详解" class="headerlink" title="4.5 关联查询详解"></a>4.5 关联查询详解</h3><p>关联查询是 MySQL 中最核心也最复杂的查询方式：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["表 A（订单表）"] --> B[" INNER JOIN\n 内连接"]    A --> C[" LEFT JOIN\n 左连接"]    A --> D[" RIGHT JOIN\n 右连接"]    A --> E[" FULL JOIN\n 全连接"]        F["表 B（用户表）"] --> B    F --> C    F --> D    F --> E        B --> G["返回 A∩B\n交集部分"]    C --> H["返回 A∪A∩B\nA的全部+B的匹配"]    D --> I["返回 A∩B∪B\nB的全部+A的匹配"]    E --> J["返回 A∪B\n并集全部"]        style G fill:#c8e6c9    style H fill:#e3f2fd    style I fill:#fff3e0    style J fill:#f8bbd0</pre></div><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 示例表结构</span></span><br><span class="line"><span class="comment">-- users: id, username, email</span></span><br><span class="line"><span class="comment">-- orders: id, order_no, user_id, total_amount, status</span></span><br><span class="line"><span class="comment">-- order_items: id, order_id, product_name, quantity, price</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- INNER JOIN：只返回两边匹配到的记录</span></span><br><span class="line"><span class="keyword">SELECT</span> </span><br><span class="line">    u.username,</span><br><span class="line">    o.order_no,</span><br><span class="line">    o.total_amount</span><br><span class="line"><span class="keyword">FROM</span> users u</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> orders o <span class="keyword">ON</span> u.id <span class="operator">=</span> o.user_id</span><br><span class="line"><span class="keyword">WHERE</span> o.status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- LEFT JOIN：返回左表所有记录，右表没有匹配的显示 NULL</span></span><br><span class="line"><span class="keyword">SELECT</span> </span><br><span class="line">    u.username,</span><br><span class="line">    <span class="built_in">COUNT</span>(o.id) <span class="keyword">AS</span> order_count,</span><br><span class="line">    <span class="built_in">COALESCE</span>(<span class="built_in">SUM</span>(o.total_amount), <span class="number">0</span>) <span class="keyword">AS</span> total_spent</span><br><span class="line"><span class="keyword">FROM</span> users u</span><br><span class="line"><span class="keyword">LEFT</span> <span class="keyword">JOIN</span> orders o <span class="keyword">ON</span> u.id <span class="operator">=</span> o.user_id</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> u.id, u.username</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> total_spent <span class="keyword">DESC</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 多表关联查询</span></span><br><span class="line"><span class="keyword">SELECT</span> </span><br><span class="line">    u.username,</span><br><span class="line">    o.order_no,</span><br><span class="line">    oi.product_name,</span><br><span class="line">    oi.quantity,</span><br><span class="line">    oi.price</span><br><span class="line"><span class="keyword">FROM</span> users u</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> orders o <span class="keyword">ON</span> u.id <span class="operator">=</span> o.user_id</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> order_items oi <span class="keyword">ON</span> o.id <span class="operator">=</span> oi.order_id</span><br><span class="line"><span class="keyword">WHERE</span> o.status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span> <span class="keyword">AND</span> oi.quantity <span class="operator">&gt;</span> <span class="number">2</span></span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> o.created_at <span class="keyword">DESC</span>;</span><br></pre></td></tr></table></figure><hr><h2 id="五、索引：数据库的性能之钥"><a href="#五、索引：数据库的性能之钥" class="headerlink" title="五、索引：数据库的性能之钥"></a>五、索引：数据库的性能之钥</h2><h3 id="5-1-索引的概念与原理"><a href="#5-1-索引的概念与原理" class="headerlink" title="5.1 索引的概念与原理"></a>5.1 索引的概念与原理</h3><p>索引是 MySQL 中用于<strong>加速数据检索</strong>的数据结构，可以类比为书籍的目录。创建合适的索引能让查询从”遍历全表”变成”直接定位”。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["📖 全表扫描"] --> B["🤷 无索引"]    C["⚡ 索引查询"] --> D["📑 B+Tree 索引"]        B --> B1["逐行扫描 1000 行\n耗时：1000ms"]    D --> D1["二分查找 10 步\n耗时：10ms"]        style A fill:#ffcdd2    style C fill:#c8e6c9    style B1 fill:#ffcdd2    style D1 fill:#c8e6c9</pre></div><h3 id="5-2-MySQL-索引类型"><a href="#5-2-MySQL-索引类型" class="headerlink" title="5.2 MySQL 索引类型"></a>5.2 MySQL 索引类型</h3><table><thead><tr><th>索引类型</th><th>关键字</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>主键索引</strong></td><td>PRIMARY KEY</td><td>每表只有一个，不允许 NULL，自动唯一</td><td><code>PRIMARY KEY(id)</code></td></tr><tr><td><strong>唯一索引</strong></td><td>UNIQUE</td><td>字段值唯一，允许 NULL</td><td><code>UNIQUE idx_email(email)</code></td></tr><tr><td><strong>普通索引</strong></td><td>INDEX &#x2F; KEY</td><td>普通索引，无唯一性约束</td><td><code>INDEX idx_name(name)</code></td></tr><tr><td><strong>全文索引</strong></td><td>FULLTEXT</td><td>用于全文搜索，只支持 CHAR、VARCHAR、TEXT</td><td><code>FULLTEXT idx_content(content)</code></td></tr><tr><td><strong>组合索引</strong></td><td>INDEX</td><td>多字段组合，需要遵循最左前缀原则</td><td><code>INDEX idx_a_b_c(a, b, c)</code></td></tr></tbody></table><h3 id="5-3-创建索引的示例"><a href="#5-3-创建索引的示例" class="headerlink" title="5.3 创建索引的示例"></a>5.3 创建索引的示例</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建表时直接添加索引</span></span><br><span class="line"><span class="keyword">CREATE TABLE</span> products (</span><br><span class="line">    id <span class="type">BIGINT</span> <span class="keyword">PRIMARY KEY</span> AUTO_INCREMENT,</span><br><span class="line">    category_id <span class="type">BIGINT</span> <span class="keyword">NOT NULL</span>,</span><br><span class="line">    name <span class="type">VARCHAR</span>(<span class="number">100</span>) <span class="keyword">NOT NULL</span>,</span><br><span class="line">    price <span class="type">DECIMAL</span>(<span class="number">10</span>, <span class="number">2</span>),</span><br><span class="line">    description TEXT,</span><br><span class="line">    created_at DATETIME,</span><br><span class="line">    </span><br><span class="line">    <span class="comment">-- 普通索引</span></span><br><span class="line">    INDEX idx_category (category_id),</span><br><span class="line">    INDEX idx_created (created_at),</span><br><span class="line">    </span><br><span class="line">    <span class="comment">-- 组合索引（遵循最左前缀原则）</span></span><br><span class="line">    INDEX idx_cat_price (category_id, price),</span><br><span class="line">    </span><br><span class="line">    <span class="comment">-- 全文索引</span></span><br><span class="line">    FULLTEXT INDEX ft_name_desc (name, description)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 已存在表添加索引</span></span><br><span class="line"><span class="keyword">CREATE</span> INDEX idx_name <span class="keyword">ON</span> products(name);</span><br><span class="line"><span class="keyword">CREATE</span> INDEX idx_cat_price <span class="keyword">ON</span> products(category_id, price);</span><br><span class="line"><span class="keyword">CREATE</span> FULLTEXT INDEX ft_desc <span class="keyword">ON</span> products(description);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查看表的索引</span></span><br><span class="line"><span class="keyword">SHOW</span> INDEX <span class="keyword">FROM</span> products;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 删除索引</span></span><br><span class="line"><span class="keyword">DROP</span> INDEX idx_name <span class="keyword">ON</span> products;</span><br><span class="line"><span class="keyword">DROP</span> INDEX idx_created <span class="keyword">ON</span> products;</span><br></pre></td></tr></table></figure><h3 id="5-4-最左前缀原则（组合索引核心）"><a href="#5-4-最左前缀原则（组合索引核心）" class="headerlink" title="5.4 最左前缀原则（组合索引核心）"></a>5.4 最左前缀原则（组合索引核心）</h3><p>组合索引 <code>(a, b, c)</code> 的查询效率如下：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["组合索引 (a, b, c)"] --> B["✅ 可以使用索引"]    A --> C["❌ 无法使用索引"]        B --> B1["WHERE a = ?"]    B --> B2["WHERE a = ? AND b = ?"]    B --> B3["WHERE a = ? AND b = ? AND c = ?"]    B --> B4["WHERE a IN (?, ?) AND b = ?"]    B --> B5["WHERE a = ? AND b > ?"]         C --> C1["WHERE b = ?"]    C --> C2["WHERE c = ?"]    C --> C3["WHERE b = ? AND c = ?"]        style B fill:#c8e6c9    style B1 fill:#c8e6c9    style B2 fill:#c8e6c9    style B3 fill:#c8e6c9    style B4 fill:#c8e6c9    style B5 fill:#c8e6c9    style C fill:#ffcdd2    style C1 fill:#ffcdd2    style C2 fill:#ffcdd2    style C3 fill:#ffcdd2</pre></div><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建组合索引</span></span><br><span class="line"><span class="keyword">CREATE</span> INDEX idx_status_created <span class="keyword">ON</span> orders(status, created_at);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 这些查询能用到索引（最左前缀原则）</span></span><br><span class="line">EXPLAIN <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;pending&#x27;</span>;</span><br><span class="line">EXPLAIN <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;pending&#x27;</span> <span class="keyword">AND</span> created_at <span class="operator">&gt;</span> <span class="string">&#x27;2026-01-01&#x27;</span>;</span><br><span class="line">EXPLAIN <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span> <span class="keyword">AND</span> created_at <span class="keyword">BETWEEN</span> <span class="string">&#x27;2026-01-01&#x27;</span> <span class="keyword">AND</span> <span class="string">&#x27;2026-06-01&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 这些查询无法用到索引</span></span><br><span class="line">EXPLAIN <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> created_at <span class="operator">&gt;</span> <span class="string">&#x27;2026-01-01&#x27;</span>;  <span class="comment">-- 跳过最左列</span></span><br><span class="line">EXPLAIN <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;pending&#x27;</span> <span class="keyword">AND</span> created_at <span class="operator">&gt;</span> <span class="string">&#x27;2026-01-01&#x27;</span> <span class="keyword">AND</span> status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span>;  <span class="comment">-- 顺序颠倒</span></span><br></pre></td></tr></table></figure><h3 id="5-5-索引使用建议"><a href="#5-5-索引使用建议" class="headerlink" title="5.5 索引使用建议"></a>5.5 索引使用建议</h3><table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>WHERE 条件字段</strong></td><td>频繁出现在 WHERE 子句中的字段建索引</td></tr><tr><td><strong>ORDER BY 字段</strong></td><td>排序字段建索引可避免filesort</td></tr><tr><td><strong>JOIN 连接字段</strong></td><td>外键字段建索引，加快连接速度</td></tr><tr><td><strong>高基数列</strong></td><td>选择性高的列（值分布广）更适合建索引</td></tr><tr><td><strong>低基数列</strong></td><td>如性别、状态，不适合单独建索引</td></tr><tr><td><strong>组合索引顺序</strong></td><td>将选择性高的列放前面</td></tr></tbody></table><blockquote><p>⚠️ <strong>注意</strong>：索引不是越多越好！每个索引都会占用磁盘空间，且增删改时需要维护索引，降低写操作性能。</p></blockquote><hr><h2 id="六、事务：数据一致性的守护者"><a href="#六、事务：数据一致性的守护者" class="headerlink" title="六、事务：数据一致性的守护者"></a>六、事务：数据一致性的守护者</h2><h3 id="6-1-事务的概念"><a href="#6-1-事务的概念" class="headerlink" title="6.1 事务的概念"></a>6.1 事务的概念</h3><p>事务（Transaction）是<strong>一组原子性的 SQL 操作</strong>，要么全部成功，要么全部失败。事务是数据库区别于文件系统的关键特性之一。</p><h3 id="6-2-事务的四大特性（ACID）"><a href="#6-2-事务的四大特性（ACID）" class="headerlink" title="6.2 事务的四大特性（ACID）"></a>6.2 事务的四大特性（ACID）</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["事务特性 ACID"] --> A1["A - Atomicity\n原子性"]    A --> A2["C - Consistency\n一致性"]    A --> A3["I - Isolation\n隔离性"]    A --> A4["D - Durability\n持久性"]        A1 --> A1a["全部成功\n或全部回滚"]    A2 --> A2a["事务前后\n数据一致"]    A3 --> A3a["并发执行\n互不干扰"]    A4 --> A4a["提交后\n永久保存"]        style A fill:#e3f2fd    style A1 fill:#c8e6c9    style A2 fill:#c8e6c9    style A3 fill:#fff3e0    style A4 fill:#f8bbd0</pre></div><h3 id="6-3-事务的隔离级别"><a href="#6-3-事务的隔离级别" class="headerlink" title="6.3 事务的隔离级别"></a>6.3 事务的隔离级别</h3><p>MySQL 支持四种事务隔离级别，越高的级别数据越安全，但性能越差：</p><table><thead><tr><th>隔离级别</th><th>脏读</th><th>不可重复读</th><th>幻读</th><th>性能</th></tr></thead><tbody><tr><td><strong>READ UNCOMMITTED</strong></td><td>✅ 可能</td><td>✅ 可能</td><td>✅ 可能</td><td>最快</td></tr><tr><td><strong>READ COMMITTED</strong></td><td>❌ 不可能</td><td>✅ 可能</td><td>✅ 可能</td><td>较快</td></tr><tr><td><strong>REPEATABLE READ</strong>（默认）</td><td>❌ 不可能</td><td>❌ 不可能</td><td>✅ 可能</td><td>一般</td></tr><tr><td><strong>SERIALIZABLE</strong></td><td>❌ 不可能</td><td>❌ 不可能</td><td>❌ 不可能</td><td>最慢</td></tr></tbody></table><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 查看当前会话隔离级别</span></span><br><span class="line"><span class="keyword">SELECT</span> @<span class="variable">@tx_isolation</span>;</span><br><span class="line"><span class="keyword">SELECT</span> @<span class="variable">@transaction_isolation</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 设置隔离级别（会话级别）</span></span><br><span class="line"><span class="keyword">SET</span> SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 设置隔离级别（全局级别）</span></span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 开启事务</span></span><br><span class="line"><span class="keyword">START</span> TRANSACTION;  <span class="comment">-- 或 BEGIN;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 转账示例：原子性操作的典型场景</span></span><br><span class="line"><span class="keyword">START</span> TRANSACTION;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 从 A 账户扣款</span></span><br><span class="line"><span class="keyword">UPDATE</span> accounts <span class="keyword">SET</span> balance <span class="operator">=</span> balance <span class="operator">-</span> <span class="number">1000</span> <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="number">1</span> <span class="keyword">AND</span> balance <span class="operator">&gt;=</span> <span class="number">1000</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 给 B 账户充值</span></span><br><span class="line"><span class="keyword">UPDATE</span> accounts <span class="keyword">SET</span> balance <span class="operator">=</span> balance <span class="operator">+</span> <span class="number">1000</span> <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 判断操作是否成功，决定提交或回滚</span></span><br><span class="line"><span class="comment">-- 如果 A 余额不足，第一条 UPDATE 影响行数为 0，此时应该回滚</span></span><br><span class="line">IF (@<span class="variable">@row_count</span> <span class="operator">=</span> <span class="number">0</span>) <span class="keyword">THEN</span></span><br><span class="line">    <span class="keyword">ROLLBACK</span>;</span><br><span class="line"><span class="keyword">ELSE</span></span><br><span class="line">    <span class="keyword">COMMIT</span>;</span><br><span class="line"><span class="keyword">END</span> IF;</span><br></pre></td></tr></table></figure><h3 id="6-4-并发事务带来的问题"><a href="#6-4-并发事务带来的问题" class="headerlink" title="6.4 并发事务带来的问题"></a>6.4 并发事务带来的问题</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["并发问题"] --> B["脏读\nDirty Read"]    A --> C["不可重复读\nNon-repeatable Read"]    A --> D["幻读\nPhantom Read"]        B --> B1["读取了其他事务\n未提交的数据"]    C --> C1["同一事务中两次读取\n同一数据结果不同"]    D --> D1["同一事务中两次查询\n结果集不一致\n（多了或少了行）"]        style A fill:#e3f2fd    style B fill:#ffcdd2    style C fill:#fff3e0    style D fill:#f8bbd0</pre></div><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 脏读示例（READ UNCOMMITTED 级别）</span></span><br><span class="line"><span class="comment">-- Session 1:</span></span><br><span class="line"><span class="keyword">BEGIN</span>;</span><br><span class="line"><span class="keyword">UPDATE</span> users <span class="keyword">SET</span> status <span class="operator">=</span> <span class="number">0</span> <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;  <span class="comment">-- 未提交</span></span><br><span class="line"><span class="comment">-- Session 2:</span></span><br><span class="line"><span class="keyword">SELECT</span> status <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;  <span class="comment">-- 读到脏数据：status = 0</span></span><br><span class="line"><span class="comment">-- Session 1:</span></span><br><span class="line"><span class="keyword">ROLLBACK</span>;  <span class="comment">-- 数据回滚，但 Session 2 已经基于错误数据做了判断</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 不可重复读示例（READ COMMITTED 级别）</span></span><br><span class="line"><span class="comment">-- Session 1:</span></span><br><span class="line"><span class="keyword">BEGIN</span>;</span><br><span class="line"><span class="keyword">SELECT</span> balance <span class="keyword">FROM</span> accounts <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="number">1</span>;  <span class="comment">-- 第一次读取：1000</span></span><br><span class="line"><span class="comment">-- Session 2（在另一个连接中）:</span></span><br><span class="line"><span class="keyword">UPDATE</span> accounts <span class="keyword">SET</span> balance <span class="operator">=</span> <span class="number">2000</span> <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">COMMIT</span>;</span><br><span class="line"><span class="comment">-- Session 1:</span></span><br><span class="line"><span class="keyword">SELECT</span> balance <span class="keyword">FROM</span> accounts <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="number">1</span>;  <span class="comment">-- 第二次读取：2000，数据变了！</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 幻读示例（REPEATABLE READ 级别）</span></span><br><span class="line"><span class="comment">-- Session 1:</span></span><br><span class="line"><span class="keyword">BEGIN</span>;</span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;pending&#x27;</span>;  <span class="comment">-- 第一次查询：3 条</span></span><br><span class="line"><span class="comment">-- Session 2:</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> orders <span class="keyword">VALUES</span> (<span class="number">4</span>, <span class="string">&#x27;pending&#x27;</span>);  <span class="comment">-- 插入新订单</span></span><br><span class="line"><span class="keyword">COMMIT</span>;</span><br><span class="line"><span class="comment">-- Session 1:</span></span><br><span class="line"><span class="keyword">UPDATE</span> orders <span class="keyword">SET</span> status <span class="operator">=</span> <span class="string">&#x27;cancelled&#x27;</span> <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;pending&#x27;</span>;  <span class="comment">-- 更新所有待处理订单</span></span><br><span class="line"><span class="comment">-- 此时可能：原 3 条被更新 + 新插入的 1 条也被更新 = 4 条更新，好像&quot;幻影&quot;被读取了</span></span><br></pre></td></tr></table></figure><h3 id="6-5-事务控制语句"><a href="#6-5-事务控制语句" class="headerlink" title="6.5 事务控制语句"></a>6.5 事务控制语句</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 开启事务</span></span><br><span class="line"><span class="keyword">BEGIN</span>;  <span class="comment">-- 或 START TRANSACTION;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 提交事务</span></span><br><span class="line"><span class="keyword">COMMIT</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 回滚事务</span></span><br><span class="line"><span class="keyword">ROLLBACK</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 设置保存点（可部分回滚）</span></span><br><span class="line"><span class="keyword">BEGIN</span>;</span><br><span class="line"><span class="keyword">INSERT INTO</span> orders (user_id, total_amount) <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="number">100</span>);</span><br><span class="line"><span class="keyword">SAVEPOINT</span> sp1;</span><br><span class="line"><span class="keyword">INSERT INTO</span> orders (user_id, total_amount) <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="number">200</span>);</span><br><span class="line"><span class="keyword">SAVEPOINT</span> sp2;</span><br><span class="line"><span class="keyword">INSERT INTO</span> orders (user_id, total_amount) <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="number">300</span>);</span><br><span class="line"><span class="keyword">ROLLBACK</span> <span class="keyword">TO</span> sp2;  <span class="comment">-- 回滚到 sp2，保留前两条插入</span></span><br><span class="line"><span class="comment">-- 此时表中有 2 条订单记录</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 释放保存点</span></span><br><span class="line"><span class="keyword">RELEASE</span> <span class="keyword">SAVEPOINT</span> sp1;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 自动提交关闭（手动控制事务）</span></span><br><span class="line"><span class="keyword">SET</span> autocommit <span class="operator">=</span> <span class="number">0</span>;  <span class="comment">-- 关闭自动提交，每个 SQL 都是独立事务</span></span><br><span class="line"><span class="keyword">SET</span> autocommit <span class="operator">=</span> <span class="number">1</span>;  <span class="comment">-- 开启自动提交</span></span><br></pre></td></tr></table></figure><hr><h2 id="七、查询优化与执行计划"><a href="#七、查询优化与执行计划" class="headerlink" title="七、查询优化与执行计划"></a>七、查询优化与执行计划</h2><h3 id="7-1-使用-EXPLAIN-分析查询"><a href="#7-1-使用-EXPLAIN-分析查询" class="headerlink" title="7.1 使用 EXPLAIN 分析查询"></a>7.1 使用 EXPLAIN 分析查询</h3><p><code>EXPLAIN</code> 是 MySQL 提供的查询分析工具，可以让你看到 MySQL 如何执行 SQL 语句，是优化查询的第一步。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基本语法</span></span><br><span class="line">EXPLAIN <span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> email <span class="operator">=</span> <span class="string">&#x27;test@example.com&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 扩展 EXPLAIN（MySQL 5.6+）</span></span><br><span class="line">EXPLAIN ANALYZE <span class="keyword">SELECT</span> ...  <span class="comment">-- 会显示实际执行时间和成本</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查看更详细的信息</span></span><br><span class="line">EXPLAIN EXTENDED <span class="keyword">SELECT</span> ...;</span><br><span class="line"><span class="keyword">SHOW</span> WARNINGS;  <span class="comment">-- 查看优化后的查询语句</span></span><br></pre></td></tr></table></figure><h3 id="7-2-EXPLAIN-输出字段解读"><a href="#7-2-EXPLAIN-输出字段解读" class="headerlink" title="7.2 EXPLAIN 输出字段解读"></a>7.2 EXPLAIN 输出字段解读</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">EXPLAIN <span class="keyword">SELECT</span> u.username, o.order_no </span><br><span class="line"><span class="keyword">FROM</span> users u </span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> orders o <span class="keyword">ON</span> u.id <span class="operator">=</span> o.user_id </span><br><span class="line"><span class="keyword">WHERE</span> u.status <span class="operator">=</span> <span class="number">1</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> o.created_at <span class="keyword">DESC</span> </span><br><span class="line">LIMIT <span class="number">10</span>;</span><br></pre></td></tr></table></figure><table><thead><tr><th>字段</th><th>含义</th><th>优化目标值</th></tr></thead><tbody><tr><td><strong>id</strong></td><td>查询执行的顺序，id 越大越先执行</td><td>-</td></tr><tr><td><strong>select_type</strong></td><td>查询类型（SIMPLE&#x2F;PRIMARY&#x2F;SUBQUERY&#x2F;UNION 等）</td><td>越简单越好</td></tr><tr><td><strong>table</strong></td><td>涉及的表</td><td>-</td></tr><tr><td><strong>type</strong></td><td>访问类型，<strong>关键指标</strong></td><td>system &gt; const &gt; eq_ref &gt; ref &gt; range &gt; index &gt; ALL</td></tr><tr><td><strong>possible_keys</strong></td><td>可能用到的索引</td><td>-</td></tr><tr><td><strong>key</strong></td><td>实际使用的索引</td><td>不为 NULL 表示用到索引</td></tr><tr><td><strong>key_len</strong></td><td>索引长度</td><td>越短越好</td></tr><tr><td><strong>rows</strong></td><td>预计扫描的行数</td><td>越少越好</td></tr><tr><td><strong>Extra</strong></td><td>附加信息（Using filesort&#x2F;Using index 等）</td><td>避免 Using filesort</td></tr></tbody></table><h3 id="7-3-访问类型等级"><a href="#7-3-访问类型等级" class="headerlink" title="7.3 访问类型等级"></a>7.3 访问类型等级</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["type 访问类型"] --> B["system\n（系统表，唯一一行）"]    A --> C["const\n（主键/唯一索引等值查询）"]    A --> D["eq_ref\n（唯一索引连接）"]    A --> E["ref\n（普通索引等值查询）"]    A --> F["range\n（索引范围查询）"]    A --> G["index\n（全文索引扫描）"]    A --> H["ALL\n（全表扫描，最差）"]        B --> B1["1 行"]    C --> C1["1 行"]    D --> D1["1 行/连接"]    E --> E1["多行"]    F --> F1["符合范围的多行"]    G --> G1["全索引"]    H --> H1["全表数据"]        B -.- B2["✨ 最优"]:::good    C -.- C2["✨ 优"]:::good    D -.- D2["✅ 良"]:::ok    E -.- E2["✅ 良"]:::ok    F -.- F2["⚠️ 一般"]:::warn    G -.- G2["⚠️ 差"]:::warn    H -.- H2["❌ 差"]:::bad        classDef good fill:#c8e6c9    classDef ok fill:#e3f2fd    classDef warn fill:#fff3e0    classDef bad fill:#ffcdd2</pre></div><h3 id="7-4-常见优化技巧"><a href="#7-4-常见优化技巧" class="headerlink" title="7.4 常见优化技巧"></a>7.4 常见优化技巧</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 1. 避免 SELECT *，只查询需要的字段</span></span><br><span class="line"><span class="comment">-- ❌ 低效</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"><span class="comment">-- ✅ 高效</span></span><br><span class="line"><span class="keyword">SELECT</span> id, order_no, total_amount <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 2. 使用 LIMIT 限制返回行数</span></span><br><span class="line"><span class="comment">-- ❌ 低效</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span>;</span><br><span class="line"><span class="comment">-- ✅ 高效</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="string">&#x27;paid&#x27;</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> created_at <span class="keyword">DESC</span> LIMIT <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 3. 使用覆盖索引（Extra: Using index）</span></span><br><span class="line"><span class="comment">-- ❌ 低效（回表查询）</span></span><br><span class="line"><span class="keyword">SELECT</span> email, phone <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"><span class="comment">-- ✅ 高效（索引包含所有字段，无需回表）</span></span><br><span class="line"><span class="keyword">CREATE</span> INDEX idx_id_phone_email <span class="keyword">ON</span> users(id, phone, email);</span><br><span class="line"><span class="keyword">SELECT</span> phone, email <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 4. 避免前置通配符（导致索引失效）</span></span><br><span class="line"><span class="comment">-- ❌ 低效（无法使用索引）</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> phone <span class="keyword">LIKE</span> <span class="string">&#x27;%138%&#x27;</span>;</span><br><span class="line"><span class="comment">-- ✅ 高效（可以使用索引）</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> phone <span class="keyword">LIKE</span> <span class="string">&#x27;138%&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 5. 使用延迟关联优化大表分页</span></span><br><span class="line"><span class="comment">-- ❌ 低效（深度分页，OFFSET 大时性能差）</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">ORDER</span> <span class="keyword">BY</span> id LIMIT <span class="number">1000000</span>, <span class="number">10</span>;</span><br><span class="line"><span class="comment">-- ✅ 高效（延迟关联，先定位 ID 再关联）</span></span><br><span class="line"><span class="keyword">SELECT</span> o.<span class="operator">*</span> <span class="keyword">FROM</span> orders o </span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> (</span><br><span class="line">    <span class="keyword">SELECT</span> id <span class="keyword">FROM</span> orders <span class="keyword">ORDER</span> <span class="keyword">BY</span> id LIMIT <span class="number">1000000</span>, <span class="number">10</span></span><br><span class="line">) <span class="keyword">AS</span> t <span class="keyword">ON</span> o.id <span class="operator">=</span> t.id;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 6. 批量插入替代循环单条插入</span></span><br><span class="line"><span class="comment">-- ❌ 低效</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> orders (user_id, total_amount) <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="number">100</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> orders (user_id, total_amount) <span class="keyword">VALUES</span> (<span class="number">2</span>, <span class="number">200</span>);</span><br><span class="line"><span class="comment">-- ✅ 高效</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> orders (user_id, total_amount) <span class="keyword">VALUES</span> </span><br><span class="line">(<span class="number">1</span>, <span class="number">100</span>), (<span class="number">2</span>, <span class="number">200</span>), (<span class="number">3</span>, <span class="number">300</span>), (<span class="number">4</span>, <span class="number">400</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 7. 使用 EXPLAIN 检查慢查询</span></span><br><span class="line"><span class="keyword">SHOW</span> VARIABLES <span class="keyword">LIKE</span> <span class="string">&#x27;slow_query_log%&#x27;</span>;</span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> slow_query_log <span class="operator">=</span> <span class="string">&#x27;ON&#x27;</span>;</span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> long_query_time <span class="operator">=</span> <span class="number">1</span>;  <span class="comment">-- 超过 1 秒的查询记录到慢查询日志</span></span><br></pre></td></tr></table></figure><hr><h2 id="八、备份与恢复"><a href="#八、备份与恢复" class="headerlink" title="八、备份与恢复"></a>八、备份与恢复</h2><h3 id="8-1-常用备份方式"><a href="#8-1-常用备份方式" class="headerlink" title="8.1 常用备份方式"></a>8.1 常用备份方式</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["MySQL 备份方式"] --> B["物理备份\n冷备份"]    A --> C["逻辑备份\nmysqldump"]    A --> D["增量备份\nbinlog"]        B --> B1["直接复制\n数据文件"]    C --> C1["导出 SQL\n脚本"]    D --> D1["记录变化\n增量恢复"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0</pre></div><h3 id="8-2-mysqldump-逻辑备份"><a href="#8-2-mysqldump-逻辑备份" class="headerlink" title="8.2 mysqldump 逻辑备份"></a>8.2 mysqldump 逻辑备份</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 备份单个数据库</span></span><br><span class="line">mysqldump -u root -p my_blog &gt; /backup/my_blog_$(<span class="built_in">date</span> +%Y%m%d).sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 备份所有数据库</span></span><br><span class="line">mysqldump -u root -p --all-databases &gt; /backup/all_db_$(<span class="built_in">date</span> +%Y%m%d).sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 备份指定表</span></span><br><span class="line">mysqldump -u root -p my_blog <span class="built_in">users</span> orders &gt; /backup/my_blog_tables.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 只备份结构（不含数据）</span></span><br><span class="line">mysqldump -u root -p my_blog --no-data &gt; /backup/my_blog_schema.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 只备份数据（不含结构）</span></span><br><span class="line">mysqldump -u root -p my_blog --no-create-info &gt; /backup/my_blog_data.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 远程备份</span></span><br><span class="line">mysqldump -h 192.168.1.100 -P 3306 -u root -p my_blog &gt; /backup/remote_db.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 压缩备份（节省空间）</span></span><br><span class="line">mysqldump -u root -p my_blog | gzip &gt; /backup/my_blog_$(<span class="built_in">date</span> +%Y%m%d).sql.gz</span><br></pre></td></tr></table></figure><h3 id="8-3-数据恢复"><a href="#8-3-数据恢复" class="headerlink" title="8.3 数据恢复"></a>8.3 数据恢复</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 恢复数据库</span></span><br><span class="line">mysql -u root -p my_blog &lt; /backup/my_blog_20260524.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 恢复压缩的备份</span></span><br><span class="line">gunzip &lt; /backup/my_blog_20260524.sql.gz | mysql -u root -p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从全量备份恢复指定数据库</span></span><br><span class="line">mysql -u root -p --one-database my_blog &lt; /backup/all_db_20260524.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># 恢复指定表</span></span><br><span class="line">mysql -u root -p my_blog &lt; /backup/my_blog_tables.sql</span><br></pre></td></tr></table></figure><h3 id="8-4-增量备份与-binlog"><a href="#8-4-增量备份与-binlog" class="headerlink" title="8.4 增量备份与 binlog"></a>8.4 增量备份与 binlog</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看 binlog 文件列表</span></span><br><span class="line">SHOW MASTER STATUS;</span><br><span class="line">SHOW BINARY LOGS;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 binlog 内容（用于恢复）</span></span><br><span class="line">mysqlbinlog /var/log/mysql/mysql-bin.000001</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基于时间点恢复</span></span><br><span class="line">mysqlbinlog --stop-datetime=<span class="string">&quot;2026-05-24 10:00:00&quot;</span> /var/log/mysql/mysql-bin.000001 | mysql -u root -p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基于位置点恢复</span></span><br><span class="line">mysqlbinlog --start-position=100 --stop-position=500 /var/log/mysql/mysql-bin.000001 | mysql -u root -p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定期备份策略建议</span></span><br><span class="line"><span class="comment"># - 每天全量备份 + 实时增量备份（binlog）</span></span><br><span class="line"><span class="comment"># - 备份文件保留 7 天以上</span></span><br><span class="line"><span class="comment"># - 定期在测试环境验证备份可用性</span></span><br></pre></td></tr></table></figure><h3 id="8-5-使用工具进行备份"><a href="#8-5-使用工具进行备份" class="headerlink" title="8.5 使用工具进行备份"></a>8.5 使用工具进行备份</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 使用 MySQL Enterprise Backup（企业版）</span></span><br><span class="line">mysqlbackup <span class="comment">--user=root --password=xxx --backup-dir=/backup/ backup</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 使用 Percona XtraBackup（开源免费，功能强大）</span></span><br><span class="line">xtrabackup <span class="comment">--backup --target-dir=/backup/full_backup/</span></span><br><span class="line">xtrabackup <span class="comment">--prepare --target-dir=/backup/full_backup/</span></span><br><span class="line">xtrabackup <span class="comment">--copy-back --target-dir=/backup/full_backup/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 使用 Liquibase（适合版本化管理数据库变更）</span></span><br><span class="line">liquibase <span class="comment">--changeLogFile=changelog.xml update</span></span><br></pre></td></tr></table></figure><hr><h2 id="九、总结与知识框架"><a href="#九、总结与知识框架" class="headerlink" title="九、总结与知识框架"></a>九、总结与知识框架</h2><h3 id="9-1-核心知识点回顾"><a href="#9-1-核心知识点回顾" class="headerlink" title="9.1 核心知识点回顾"></a>9.1 核心知识点回顾</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((MySQL 核心知识))    数据库基础      存储引擎      数据类型      表操作      约束    CRUD 操作      INSERT      SELECT      UPDATE      DELETE      关联查询    索引      主键/唯一/普通/全文      组合索引      最左前缀原则      索引优化    事务      ACID 特性      隔离级别      并发问题      锁机制    查询优化      EXPLAIN      访问类型      性能技巧      慢查询日志    备份恢复      mysqldump      binlog      全量/增量备份</pre></div><h3 id="9-2-学习路线建议"><a href="#9-2-学习路线建议" class="headerlink" title="9.2 学习路线建议"></a>9.2 学习路线建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["入门阶段"] --> B["基础阶段"]    B --> C["进阶阶段"]    C --> D["精通阶段"]        A --> A1["安装配置"]    A --> A2["SQL 基础"]    A --> A3["CRUD 操作"]        B --> B1["索引原理"]    B --> B2["事务概念"]    B --> B3["关联查询"]        C --> C1["查询优化"]    C --> C2["锁机制"]    C --> C3["主从复制"]        D --> D1["性能调优"]    D --> D2["集群架构"]    D --> D3["源码阅读"]        style A fill:#e3f2fd    style B fill:#c8e6c9    style C fill:#fff3e0    style D fill:#f8bbd0</pre></div><h3 id="9-3-推荐学习资源"><a href="#9-3-推荐学习资源" class="headerlink" title="9.3 推荐学习资源"></a>9.3 推荐学习资源</h3><ul><li>📖 <strong>官方文档</strong>：<a href="https://dev.mysql.com/doc/refman/8.0/en/">MySQL 8.0 Reference Manual</a></li><li>📖 <strong>《高性能 MySQL》</strong>：MySQL 领域的经典著作，必读！</li><li>💻 <strong>在线练习</strong>：<a href="http://sqlfiddle.com/">SQL Fiddle</a>、<a href="https://leetcode.cn/problemset/database/">LeetCode SQL</a></li></ul><hr><blockquote><p>💡 <strong>写给读者的话</strong>：MySQL 学习没有捷径，多敲 SQL、多踩坑、多总结。内功深厚的程序员，一定是那些对数据库原理有深刻理解的人。纸上得来终觉浅，绝知此事要躬行，去真实环境中练习吧！</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 24 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/mysql-complete-guide/</id>
    <link href="https://blog.codenav.top/mysql-complete-guide/"/>
    <published>2026-05-24T05:27:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="MySQL-数据库从入门到精通：核心知识点全覆盖-🗄️"><a href="#MySQL-数据库从入门到精通：核心知识点全覆盖-🗄️" class="headerlink" title="MySQL 数据库从入门到精通：核心知识点全覆盖 🗄️"></a>My]]>
    </summary>
    <title>MySQL 数据库从入门到精通：核心知识点全覆盖</title>
    <updated>2026-05-24T05:28:10.657Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="技术总结" scheme="https://blog.codenav.top/categories/%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB%93/"/>
    <category term="数据结构" scheme="https://blog.codenav.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    <category term="算法" scheme="https://blog.codenav.top/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<h1 id="数据结构核心总结：线性表、链表、栈与队列-🚀"><a href="#数据结构核心总结：线性表、链表、栈与队列-🚀" class="headerlink" title="数据结构核心总结：线性表、链表、栈与队列 🚀"></a>数据结构核心总结：线性表、链表、栈与队列 🚀</h1><blockquote><p>数据结构是计算机存储、组织数据的方式。好的数据结构能够显著提升程序的运行效率。本文将系统总结<strong>线性表</strong>、<strong>链表</strong>、<strong>栈</strong>和<strong>队列</strong>这四种最基础且应用最广泛的数据结构，帮助你建立完整的知识体系。</p></blockquote><hr><h2 id="📖-目录"><a href="#📖-目录" class="headerlink" title="📖 目录"></a>📖 目录</h2><ol><li><a href="#%E7%BA%BF%E6%80%A7%E8%A1%A8%E5%9F%BA%E7%A1%80">线性表基础</a></li><li><a href="#%E9%A1%BA%E5%BA%8F%E8%A1%A8%E4%B8%8E%E9%93%BE%E8%A1%A8">顺序表与链表</a></li><li><a href="#%E5%8D%95%E5%90%91%E9%93%BE%E8%A1%A8">单向链表</a></li><li><a href="#%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8%E4%B8%8E%E5%BE%AA%E7%8E%AF%E9%93%BE%E8%A1%A8">双向链表与循环链表</a></li><li><a href="#%E6%A0%88">栈</a></li><li><a href="#%E9%98%9F%E5%88%97">队列</a></li><li><a href="#%E6%80%BB%E7%BB%93%E5%AF%B9%E6%AF%94">总结对比</a></li></ol><hr><h2 id="线性表基础"><a href="#线性表基础" class="headerlink" title="线性表基础"></a>线性表基础</h2><h3 id="什么是线性表？"><a href="#什么是线性表？" class="headerlink" title="什么是线性表？"></a>什么是线性表？</h3><p><strong>线性表（Linear List）</strong> 是由 n 个具有相同特性的数据元素组成的<strong>有序序列</strong>。它是数据结构中最简单、最基本的一种结构。</p><p>线性表的基本特征：</p><ul><li>存在唯一的”第一个”元素</li><li>存在唯一的”最后一个”元素</li><li>除第一个元素外，每个元素都有且仅有一个前驱元素</li><li>除最后一个元素外，每个元素都有且仅有一个后继元素</li></ul><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    A["元素1"] --> B["元素2"]    B --> C["元素3"]    C --> D["元素4"]    E["元素n-1"] --> F["元素n"]    style A fill:#90EE90    style F fill:#FFB6C1</pre></div><h3 id="线性表的抽象数据类型"><a href="#线性表的抽象数据类型" class="headerlink" title="线性表的抽象数据类型"></a>线性表的抽象数据类型</h3><p>线性表的操作主要包括：</p><table><thead><tr><th>操作</th><th>说明</th></tr></thead><tbody><tr><td><code>initList()</code></td><td>初始化线性表</td></tr><tr><td><code>length()</code></td><td>获取线性表长度</td></tr><tr><td><code>get(i)</code></td><td>获取第 i 个位置的元素</td></tr><tr><td><code>insert(i, e)</code></td><td>在第 i 个位置插入元素 e</td></tr><tr><td><code>delete(i)</code></td><td>删除第 i 个位置的元素</td></tr><tr><td><code>isEmpty()</code></td><td>判断线性表是否为空</td></tr><tr><td><code>clear()</code></td><td>清空线性表</td></tr></tbody></table><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["开始"] --> B{线性表是否为空?}    B -->|是| C["执行特殊处理"]    B -->|否| D["查找目标位置"]    D --> E{位置有效?}    E -->|是| F["执行插入/删除操作"]    E -->|否| G["返回错误"]    F --> H["长度变化"]    H --> I["结束"]    C --> I    G --> I</pre></div><hr><h2 id="顺序表与链表"><a href="#顺序表与链表" class="headerlink" title="顺序表与链表"></a>顺序表与链表</h2><p>线性表有两种主要的存储方式：<strong>顺序存储</strong>和<strong>链式存储</strong>。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["线性表"] --> B["顺序表"]    A --> C["链表"]    B --> D["使用连续内存空间存储"]    C --> E["使用指针连接各元素"]    D --> F["随机访问 O1"]    E --> G["插入删除 On"]    F --> H["空间固定"]    G --> I["空间动态"]</pre></div><h3 id="顺序表（Sequential-List）"><a href="#顺序表（Sequential-List）" class="headerlink" title="顺序表（Sequential List）"></a>顺序表（Sequential List）</h3><p>顺序表使用一段<strong>连续的存储单元</strong>依次存储线性表中的元素。</p><p><strong>优点：</strong></p><ul><li>🔥 随机访问能力强，查询速度快（时间复杂度 O(1)）</li><li>🔥 缓存命中率高，因为内存连续</li></ul><p><strong>缺点：</strong></p><ul><li>❌ 插入和删除操作需要移动大量元素（时间复杂度 O(n)）</li><li>❌ 存储空间固定，大小受限制</li><li>❌ 需要预先分配足够的连续内存空间</li></ul><h3 id="链表（Linked-List）"><a href="#链表（Linked-List）" class="headerlink" title="链表（Linked List）"></a>链表（Linked List）</h3><p>链表通过<strong>指针</strong>将一系列元素连接起来，每个元素包含数据域和指针域。</p><p><strong>优点：</strong></p><ul><li>✅ 插入和删除操作不需要移动元素（时间复杂度 O(1)）</li><li>✅ 不需要预先分配固定大小的空间</li><li>✅ 可以动态扩展</li></ul><p><strong>缺点：</strong></p><ul><li>❌ 不支持随机访问，查询需要遍历（时间复杂度 O(n)）</li><li>❌ 每个元素需要额外的指针空间</li><li>❌ 缓存命中率较低</li></ul><hr><h2 id="单向链表"><a href="#单向链表" class="headerlink" title="单向链表"></a>单向链表</h2><h3 id="单向链表结构"><a href="#单向链表结构" class="headerlink" title="单向链表结构"></a>单向链表结构</h3><p>单向链表（Singly Linked List）是链表中最简单的一种，每个节点包含<strong>数据域</strong>和<strong>指向下一个节点的指针</strong>。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    subgraph 单向链表        A["HEAD"] --> B["Node1|Data: 10|Next: →"]        B --> C["Node2|Data: 20|Next: →"]        C --> D["Node3|Data: 30|Next: →"]        D --> E["NULL"]    end    style A fill:#87CEEB    style E fill:#FFB6C1</pre></div><h3 id="Java-实现单向链表"><a href="#Java-实现单向链表" class="headerlink" title="Java 实现单向链表"></a>Java 实现单向链表</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 单向链表节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">    E data;       <span class="comment">// 数据域</span></span><br><span class="line">    Node&lt;E&gt; next; <span class="comment">// 指针域，指向下一个节点</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(E data)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.data = data;</span><br><span class="line">        <span class="built_in">this</span>.next = <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 单向链表实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> Node&lt;E&gt; head; <span class="comment">// 头节点</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> size;     <span class="comment">// 链表长度</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SinglyLinkedList</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.head = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(<span class="literal">null</span>); <span class="comment">// 虚拟头节点</span></span><br><span class="line">        <span class="built_in">this</span>.size = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在链表头部添加元素</span></span><br><span class="line"><span class="comment">     * 时间复杂度：O(1)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(E element)</span> &#123;</span><br><span class="line">        Node&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(element);</span><br><span class="line">        newNode.next = head.next;</span><br><span class="line">        head.next = newNode;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在链表尾部添加元素</span></span><br><span class="line"><span class="comment">     * 时间复杂度：O(n)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(E element)</span> &#123;</span><br><span class="line">        Node&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(element);</span><br><span class="line">        Node&lt;E&gt; current = head;</span><br><span class="line">        <span class="keyword">while</span> (current.next != <span class="literal">null</span>) &#123;</span><br><span class="line">            current = current.next;</span><br><span class="line">        &#125;</span><br><span class="line">        current.next = newNode;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在指定位置插入元素</span></span><br><span class="line"><span class="comment">     * 时间复杂度：O(n)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> index, E element)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &lt; <span class="number">0</span> || index &gt; size) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>(<span class="string">&quot;Index: &quot;</span> + index + <span class="string">&quot;, Size: &quot;</span> + size);</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(element);</span><br><span class="line">        Node&lt;E&gt; current = head;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; index; i++) &#123;</span><br><span class="line">            current = current.next;</span><br><span class="line">        &#125;</span><br><span class="line">        newNode.next = current.next;</span><br><span class="line">        current.next = newNode;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除指定位置的元素</span></span><br><span class="line"><span class="comment">     * 时间复杂度：O(n)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &lt; <span class="number">0</span> || index &gt;= size) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>(<span class="string">&quot;Index: &quot;</span> + index + <span class="string">&quot;, Size: &quot;</span> + size);</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; current = head;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; index; i++) &#123;</span><br><span class="line">            current = current.next;</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; removedNode = current.next;</span><br><span class="line">        current.next = removedNode.next;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> removedNode.data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取指定位置的元素</span></span><br><span class="line"><span class="comment">     * 时间复杂度：O(n)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">get</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &lt; <span class="number">0</span> || index &gt;= size) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>(<span class="string">&quot;Index: &quot;</span> + index + <span class="string">&quot;, Size: &quot;</span> + size);</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; current = head.next;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; index; i++) &#123;</span><br><span class="line">            current = current.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> current.data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 链表长度</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断链表是否为空</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="单向链表操作图解"><a href="#单向链表操作图解" class="headerlink" title="单向链表操作图解"></a>单向链表操作图解</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    subgraph 插入操作        A1["原链表"] --> A2["在 Node2 后插入 NodeX"]        A2 --> A3["Node2.next = NodeX"]        A3 --> A4["NodeX.next = Node3"]    end    subgraph 删除操作        B1["原链表"] --> B2["删除 Node2"]        B2 --> B3["Node1.next = Node3"]        B3 --> B4["释放 Node2"]    end</pre></div><hr><h2 id="双向链表与循环链表"><a href="#双向链表与循环链表" class="headerlink" title="双向链表与循环链表"></a>双向链表与循环链表</h2><h3 id="双向链表"><a href="#双向链表" class="headerlink" title="双向链表"></a>双向链表</h3><p>双向链表（Doubly Linked List）的每个节点包含<strong>数据域</strong>、<strong>前驱指针</strong>和<strong>后继指针</strong>。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    A["NULL"] <--> B["Node1|Data: 10|Prev: ←|Next: →"]    B <--> C["Node2|Data: 20|Prev: ←|Next: →"]    C <--> D["Node3|Data: 30|Prev: ←|Next: →"]    D <--> E["NULL"]    style A fill:#FFB6C1    style E fill:#FFB6C1</pre></div><h3 id="双向链表-Java-实现"><a href="#双向链表-Java-实现" class="headerlink" title="双向链表 Java 实现"></a>双向链表 Java 实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 双向链表节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DNode</span>&lt;E&gt; &#123;</span><br><span class="line">    E data;</span><br><span class="line">    DNode&lt;E&gt; prev; <span class="comment">// 前驱指针</span></span><br><span class="line">    DNode&lt;E&gt; next; <span class="comment">// 后继指针</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DNode</span><span class="params">(E data)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.data = data;</span><br><span class="line">        <span class="built_in">this</span>.prev = <span class="literal">null</span>;</span><br><span class="line">        <span class="built_in">this</span>.next = <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 双向链表实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DoublyLinkedList</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> DNode&lt;E&gt; head; <span class="comment">// 头节点</span></span><br><span class="line">    <span class="keyword">private</span> DNode&lt;E&gt; tail; <span class="comment">// 尾节点</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DoublyLinkedList</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.head = <span class="keyword">new</span> <span class="title class_">DNode</span>&lt;&gt;(<span class="literal">null</span>);</span><br><span class="line">        <span class="built_in">this</span>.tail = <span class="keyword">new</span> <span class="title class_">DNode</span>&lt;&gt;(<span class="literal">null</span>);</span><br><span class="line">        <span class="built_in">this</span>.head.next = tail;</span><br><span class="line">        <span class="built_in">this</span>.tail.prev = head;</span><br><span class="line">        <span class="built_in">this</span>.size = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在头部添加元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(E element)</span> &#123;</span><br><span class="line">        DNode&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">DNode</span>&lt;&gt;(element);</span><br><span class="line">        newNode.next = head.next;</span><br><span class="line">        newNode.prev = head;</span><br><span class="line">        head.next.prev = newNode;</span><br><span class="line">        head.next = newNode;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在尾部添加元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(E element)</span> &#123;</span><br><span class="line">        DNode&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">DNode</span>&lt;&gt;(element);</span><br><span class="line">        newNode.next = tail;</span><br><span class="line">        newNode.prev = tail.prev;</span><br><span class="line">        tail.prev.next = newNode;</span><br><span class="line">        tail.prev = newNode;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除指定节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(DNode&lt;E&gt; node)</span> &#123;</span><br><span class="line">        node.prev.next = node.next;</span><br><span class="line">        node.next.prev = node.prev;</span><br><span class="line">        size--;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="循环链表"><a href="#循环链表" class="headerlink" title="循环链表"></a>循环链表</h3><p>循环链表（Circular Linked List）是链表的一种变形，<strong>最后一个节点的指针指向头节点</strong>，形成环状结构。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    A["HEAD"] --> B["Node1"]    B --> C["Node2"]    C --> D["Node3"]    D --> A    style A fill:#87CEEB</pre></div><p><strong>循环链表的特点：</strong></p><ul><li>🔄 没有 null 指针，所有节点形成闭环</li><li>🔄 从任意节点出发，都可以遍历整个链表</li><li>🔄 常用于需要循环遍历的场景（如约瑟夫问题）</li></ul><hr><h2 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h2><h3 id="什么是栈？"><a href="#什么是栈？" class="headerlink" title="什么是栈？"></a>什么是栈？</h3><p><strong>栈（Stack）</strong> 是一种特殊的线性表，它遵循 <strong>LIFO（Last In First Out）</strong> 原则——后进先出。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["入栈 Push"] --> B["栈 Stack"]    B --> C["出栈 Pop"]    A2["元素 D"] -->|压入| B    A3["元素 C"] -->|压入| B    A4["元素 B"] -->|压入| B    A5["元素 A"] -->|压入| B    B -->|弹出| R1["元素 D"]    style B fill:#FFD700</pre></div><h3 id="栈的基本操作"><a href="#栈的基本操作" class="headerlink" title="栈的基本操作"></a>栈的基本操作</h3><table><thead><tr><th>操作</th><th>说明</th><th>时间复杂度</th></tr></thead><tbody><tr><td><code>push(e)</code></td><td>将元素压入栈顶</td><td>O(1)</td></tr><tr><td><code>pop()</code></td><td>弹出栈顶元素</td><td>O(1)</td></tr><tr><td><code>peek()</code></td><td>查看栈顶元素</td><td>O(1)</td></tr><tr><td><code>isEmpty()</code></td><td>判断栈是否为空</td><td>O(1)</td></tr><tr><td><code>size()</code></td><td>获取栈的大小</td><td>O(1)</td></tr></tbody></table><h3 id="Java-栈实现"><a href="#Java-栈实现" class="headerlink" title="Java 栈实现"></a>Java 栈实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 栈接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Stack</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(E element)</span>;  <span class="comment">// 压栈</span></span><br><span class="line">    E <span class="title function_">pop</span><span class="params">()</span>;               <span class="comment">// 弹栈</span></span><br><span class="line">    E <span class="title function_">peek</span><span class="params">()</span>;              <span class="comment">// 查看栈顶</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span>;            <span class="comment">// 大小</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span>;     <span class="comment">// 是否为空</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 基于数组的栈实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayStack</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Stack</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> Object[] elements;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> top;           <span class="comment">// 栈顶指针</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> capacity;      <span class="comment">// 栈容量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayStack</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        <span class="built_in">this</span>.elements = <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">        <span class="built_in">this</span>.top = -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(E element)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (top &gt;= capacity - <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StackOverflowError</span>(<span class="string">&quot;Stack is full!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        elements[++top] = element;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Stack is empty!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (E) elements[top--];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Stack is empty!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (E) elements[top];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> top + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> top == -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="栈的应用场景"><a href="#栈的应用场景" class="headerlink" title="栈的应用场景"></a>栈的应用场景</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap    root((栈的应用))        函数调用            调用栈            递归实现        表达式求值            中缀转后缀            后缀表达式计算        括号匹配            有效括号判断        浏览器前进后退            历史记录        撤销操作            编辑器撤销</pre></div><p><strong>典型应用：</strong></p><ol><li><strong>函数调用栈</strong> - 程序语言的函数调用管理</li><li><strong>括号匹配</strong> - 检验表达式中的括号是否合法</li><li><strong>表达式求值</strong> - 中缀表达式转后缀表达式</li><li><strong>深度优先搜索（DFS）</strong> - 图和树的遍历</li></ol><h3 id="括号匹配实例"><a href="#括号匹配实例" class="headerlink" title="括号匹配实例"></a>括号匹配实例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 有效的括号匹配</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isValid</span><span class="params">(String s)</span> &#123;</span><br><span class="line">    Stack&lt;Character&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : s.toCharArray()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="string">&#x27;(&#x27;</span> || c == <span class="string">&#x27;[&#x27;</span> || c == <span class="string">&#x27;&#123;&#x27;</span>) &#123;</span><br><span class="line">            stack.push(c);  <span class="comment">// 左括号入栈</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (stack.isEmpty()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;  <span class="comment">// 没有左括号可以匹配</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">char</span> <span class="variable">top</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">            <span class="keyword">if</span> ((c == <span class="string">&#x27;)&#x27;</span> &amp;&amp; top != <span class="string">&#x27;(&#x27;</span>) ||</span><br><span class="line">                (c == <span class="string">&#x27;]&#x27;</span> &amp;&amp; top != <span class="string">&#x27;[&#x27;</span>) ||</span><br><span class="line">                (c == <span class="string">&#x27;&#125;&#x27;</span> &amp;&amp; top != <span class="string">&#x27;&#123;&#x27;</span>)) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;  <span class="comment">// 括号不匹配</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> stack.isEmpty();  <span class="comment">// 栈空说明完全匹配</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h2><h3 id="什么是队列？"><a href="#什么是队列？" class="headerlink" title="什么是队列？"></a>什么是队列？</h3><p><strong>队列（Queue）</strong> 是一种特殊的线性表，它遵循 <strong>FIFO（First In First Out）</strong> 原则——先进先出。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["入队 Enqueue"] --> B["队列 Queue"]    B --> C["出队 Dequeue"]    A2["元素 A"] -->|加入队尾| B    A3["元素 B"] -->|加入队尾| B    A4["元素 C"] -->|加入队尾| B    B -->|移除队首| R1["元素 A"]    style B fill:#87CEEB</pre></div><h3 id="队列的基本操作"><a href="#队列的基本操作" class="headerlink" title="队列的基本操作"></a>队列的基本操作</h3><table><thead><tr><th>操作</th><th>说明</th><th>时间复杂度</th></tr></thead><tbody><tr><td><code>enqueue(e)</code></td><td>将元素加入队尾</td><td>O(1)</td></tr><tr><td><code>dequeue()</code></td><td>移除队首元素</td><td>O(1)</td></tr><tr><td><code>front()</code></td><td>查看队首元素</td><td>O(1)</td></tr><tr><td><code>rear()</code></td><td>查看队尾元素</td><td>O(1)</td></tr><tr><td><code>isEmpty()</code></td><td>判断队列是否为空</td><td>O(1)</td></tr></tbody></table><h3 id="Java-队列实现"><a href="#Java-队列实现" class="headerlink" title="Java 队列实现"></a>Java 队列实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 队列接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Queue</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">enqueue</span><span class="params">(E element)</span>;  <span class="comment">// 入队</span></span><br><span class="line">    E <span class="title function_">dequeue</span><span class="params">()</span>;              <span class="comment">// 出队</span></span><br><span class="line">    E <span class="title function_">front</span><span class="params">()</span>;                <span class="comment">// 查看队首</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span>;        <span class="comment">// 是否为空</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span>;               <span class="comment">// 大小</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 基于数组的循环队列实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CircularQueue</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> Object[] elements;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> front;        <span class="comment">// 队首指针</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> rear;         <span class="comment">// 队尾指针</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> capacity;     <span class="comment">// 队列容量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CircularQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        <span class="built_in">this</span>.elements = <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">        <span class="built_in">this</span>.front = <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">this</span>.rear = <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">this</span>.size = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">enqueue</span><span class="params">(E element)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (size == capacity) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Queue is full!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        elements[rear] = element;</span><br><span class="line">        rear = (rear + <span class="number">1</span>) % capacity;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">dequeue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Queue is empty!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">element</span> <span class="operator">=</span> (E) elements[front];</span><br><span class="line">        front = (front + <span class="number">1</span>) % capacity;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> element;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">front</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Queue is empty!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (E) elements[front];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="循环队列原理图"><a href="#循环队列原理图" class="headerlink" title="循环队列原理图"></a>循环队列原理图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    subgraph 循环队列示意图        direction TB        A1["front=0"] --> A2["rear=4"]        A2 --> A3["capacity=8"]    end    subgraph 队列状态        direction LR        B1["0"] --> B2["1"]        B2 --> B3["2"]        B3 --> B4["3"]        B4 --> B5["A" fill:#90EE90]        B5 --> B6["B" fill:#90EE90]        B6 --> B7["C" fill:#90EE90]        B7 --> B8["D" fill:#90EE90]    end</pre></div><h3 id="队列的应用场景"><a href="#队列的应用场景" class="headerlink" title="队列的应用场景"></a>队列的应用场景</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap    root((队列的应用))        任务调度            线程池            打印机队列        广度优先搜索            BFS遍历            层级遍历        消息队列            异步通信            限流削峰        缓存策略            FIFO缓存            页面置换</pre></div><p><strong>典型应用：</strong></p><ol><li><strong>任务调度</strong> - 操作系统中的进程&#x2F;线程调度</li><li><strong>广度优先搜索（BFS）</strong> - 图和树的层级遍历</li><li><strong>消息队列</strong> - 异步通信、流量削峰</li><li><strong>缓存策略</strong> - LRU、LFU 等缓存算法</li></ol><h3 id="BFS-遍历实例"><a href="#BFS-遍历实例" class="headerlink" title="BFS 遍历实例"></a>BFS 遍历实例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用队列实现二叉树层序遍历</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">levelOrder</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    List&lt;List&lt;Integer&gt;&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> result;</span><br><span class="line">    </span><br><span class="line">    Queue&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    queue.offer(root);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">levelSize</span> <span class="operator">=</span> queue.size();</span><br><span class="line">        List&lt;Integer&gt; level = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; levelSize; i++) &#123;</span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">            level.add(node.val);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> (node.left != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.left);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (node.right != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        result.add(level);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><h3 id="四种数据结构对比"><a href="#四种数据结构对比" class="headerlink" title="四种数据结构对比"></a>四种数据结构对比</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["数据结构选择"] --> B{主要操作是什么?}    B -->|只需要在一端操作| C{需要 FIFO 还是 LIFO?}    C -->|LIFO| D["栈 Stack"]    C -->|FIFO| E["队列 Queue"]    B -->|需要在中间插入删除| F{需要随机访问?}    F -->|是| G["顺序表 ArrayList"]    F -->|否| H["链表 LinkedList"]    D --> I["时间复杂度 O(1)"]    E --> I    G --> J["查询 O(1) / 插入删除 O(n)"]    H --> K["查询 O(n) / 插入删除 O(1)"]</pre></div><h3 id="复杂度对比表"><a href="#复杂度对比表" class="headerlink" title="复杂度对比表"></a>复杂度对比表</h3><table><thead><tr><th>数据结构</th><th>访问</th><th>插入</th><th>删除</th><th>空间</th></tr></thead><tbody><tr><td>顺序表</td><td>O(1)</td><td>O(n)</td><td>O(n)</td><td>O(n)</td></tr><tr><td>单向链表</td><td>O(n)</td><td>O(1)*</td><td>O(1)*</td><td>O(n)</td></tr><tr><td>双向链表</td><td>O(n)</td><td>O(1)*</td><td>O(1)*</td><td>O(n)</td></tr><tr><td>栈</td><td>-</td><td>O(1)</td><td>O(1)</td><td>O(n)</td></tr><tr><td>队列</td><td>-</td><td>O(1)</td><td>O(1)</td><td>O(n)</td></tr></tbody></table><blockquote><p>*注：O(1) 的前提是已经找到目标位置，否则需要 O(n) 的查找时间。</p></blockquote><h3 id="选择建议"><a href="#选择建议" class="headerlink" title="选择建议"></a>选择建议</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>graph LR    A["频繁查询"] --> D["顺序表 / ArrayList"]    B["频繁插入删除"] --> E["链表 / LinkedList"]    C["先进先出"] --> F["队列 Queue"]    D --> G["需要平衡"]    E --> G    F --> G    G --> H["LinkedList 实现 Queue"]</pre></div><p><strong>选择要点：</strong></p><ul><li>🔥 <strong>查询多、修改少</strong> → 选择 <code>ArrayList</code>（顺序表）</li><li>🔥 <strong>插入删除多、查询少</strong> → 选择 <code>LinkedList</code>（链表）</li><li>🔥 <strong>需要 LIFO</strong> → 选择 <code>Stack</code></li><li>🔥 <strong>需要 FIFO</strong> → 选择 <code>Queue</code></li></ul><hr><h2 id="📚-最后"><a href="#📚-最后" class="headerlink" title="📚 最后"></a>📚 最后</h2><p>线性表、链表、栈、队列是数据结构中最基础的概念，看似简单，却支撑着整个计算机世界的运行。理解它们的原理和适用场景，是成为优秀程序员的必经之路。</p><p>希望本文对你有所帮助！如果有任何问题，欢迎留言讨论 💬</p><hr><p><em>原创不易，转载请注明出处！</em></p>]]>
    </content>
    <id>https://blog.codenav.top/data-structures-linear/</id>
    <link href="https://blog.codenav.top/data-structures-linear/"/>
    <published>2026-05-22T03:45:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="数据结构核心总结：线性表、链表、栈与队列-🚀"><a href="#数据结构核心总结：线性表、链表、栈与队列-🚀" class="headerlink" title="数据结构核心总结：线性表、链表、栈与队列 🚀"></a>数据结构核心总结：线性表、链表、栈]]>
    </summary>
    <title>数据结构核心总结：线性表、链表、栈与队列</title>
    <updated>2026-05-24T04:59:33.106Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="进阶" scheme="https://blog.codenav.top/tags/%E8%BF%9B%E9%98%B6/"/>
    <content>
      <![CDATA[<h1 id="Java-进阶核心知识点总结-🚀"><a href="#Java-进阶核心知识点总结-🚀" class="headerlink" title="Java 进阶核心知识点总结 🚀"></a>Java 进阶核心知识点总结 🚀</h1><blockquote><p>本文将深入探讨 Java 进阶知识，包括 JVM 内存模型、垃圾回收机制、并发编程、集合源码、设计模式等核心内容。如果你已经掌握了 Java 基础，那么进阶之路从这里开始。</p></blockquote><hr><h2 id="📖-目录"><a href="#📖-目录" class="headerlink" title="📖 目录"></a>📖 目录</h2><ol><li><a href="#jvm-%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B">JVM 内存模型</a></li><li><a href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6">垃圾回收机制</a></li><li><a href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6">类加载机制</a></li><li><a href="#%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B">并发编程</a></li><li><a href="#%E9%9B%86%E5%90%88%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90">集合源码解析</a></li><li><a href="#%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F">设计模式</a></li><li><a href="#%E5%B8%B8%E7%94%A8%E5%B7%A5%E5%85%B7%E7%B1%BB">常用工具类</a></li></ol><hr><h2 id="JVM-内存模型"><a href="#JVM-内存模型" class="headerlink" title="JVM 内存模型"></a>JVM 内存模型</h2><h3 id="为什么需要了解-JVM？"><a href="#为什么需要了解-JVM？" class="headerlink" title="为什么需要了解 JVM？"></a>为什么需要了解 JVM？</h3><p>很多初学者可能会问：我写 Java 代码又不需要直接操作内存，JVM 自动管理不就好了吗？</p><p>这个想法没错，但如果你想写出<strong>高性能</strong>、<strong>资源利用率高</strong>的代码，就必须了解 JVM。比如：</p><ul><li>什么时候对象会被回收？</li><li>为什么代码没问题但内存一直涨？</li><li>如何调优 JVM 参数？</li><li>如何排查 OOM（OutOfMemoryError）问题？</li></ul><p>这些问题都需要对 JVM 有深入了解。</p><h3 id="JVM-内存划分"><a href="#JVM-内存划分" class="headerlink" title="JVM 内存划分"></a>JVM 内存划分</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    subgraph JVM 运行时数据区        A["JVM 内存模型"] --> B["线程共享区"]        A --> C["线程私有区"]        B --> D["堆 Heap"]        B --> E["方法区 Method Area"]        C --> F["虚拟机栈 VM Stack"]        C --> G["本地方法栈 Native Stack"]        C --> H["程序计数器 PC"]    end    style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#e8f5e9</pre></div><h3 id="各区域详解"><a href="#各区域详解" class="headerlink" title="各区域详解"></a>各区域详解</h3><h4 id="1-程序计数器（PC-Register）"><a href="#1-程序计数器（PC-Register）" class="headerlink" title="1. 程序计数器（PC Register）"></a>1. 程序计数器（PC Register）</h4><p><strong>作用</strong>：当前线程所执行的字节码的行号指示器。</p><p><strong>特点</strong>：</p><ul><li>线程私有，每个线程都有自己的程序计数器</li><li>唯一一个不会发生 OutOfMemoryError 的区域</li><li>执行 Java 方法时记录字节码指令地址，执行 Native 方法时为空</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 举例：程序计数器的工作方式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PCRDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">1</span>;        <span class="comment">// 字节码指令 0: iconst_1</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">2</span>;        <span class="comment">// 字节码指令 2: iconst_2</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> a + b;    <span class="comment">// 字节码指令 3: iadd</span></span><br><span class="line">        <span class="comment">// 程序计数器记录当前执行到哪条指令</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-虚拟机栈（VM-Stack）"><a href="#2-虚拟机栈（VM-Stack）" class="headerlink" title="2. 虚拟机栈（VM Stack）"></a>2. 虚拟机栈（VM Stack）</h4><p><strong>作用</strong>：存储方法调用时的局部变量、操作数栈、动态链接等信息。</p><p><strong>特点</strong>：</p><ul><li>线程私有，生命周期与线程相同</li><li>每个方法调用都会创建一个<strong>栈帧（Stack Frame）</strong></li><li>方法执行完毕，栈帧出栈</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StackDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        methodA();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">methodA</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        methodB(a);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">methodB</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> num * <span class="number">2</span>;</span><br><span class="line">        System.out.println(result);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    subgraph 虚拟机栈        A["main 栈帧"] --> B["methodA 栈帧"]        B --> C["methodB 栈帧"]        style A fill:#e3f2fd        style B fill:#e8f5e9        style C fill:#fce4ec    end</pre></div><p><strong>常见错误</strong>：</p><ul><li><code>StackOverflowError</code>：栈溢出，通常是递归调用没有正确终止</li><li><code>OutOfMemoryError</code>：内存溢出，栈内存不足</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 栈溢出示例：递归没有终止条件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StackOverflowDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">recursive</span><span class="params">()</span> &#123;</span><br><span class="line">        recursive();  <span class="comment">// 无限递归，最终 StackOverflowError</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        recursive();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-本地方法栈（Native-Stack）"><a href="#3-本地方法栈（Native-Stack）" class="headerlink" title="3. 本地方法栈（Native Stack）"></a>3. 本地方法栈（Native Stack）</h4><p><strong>作用</strong>：为 JVM 调用本地方法（Native Method）服务。</p><p><strong>与虚拟机栈的区别</strong>：</p><ul><li>虚拟机栈为 Java 方法服务</li><li>本地方法栈为 Native 方法服务</li><li>HotSpot 虚拟机将两者合二为一</li></ul><h4 id="4-堆（Heap）"><a href="#4-堆（Heap）" class="headerlink" title="4. 堆（Heap）"></a>4. 堆（Heap）</h4><p><strong>作用</strong>：几乎所有对象实例和数组都在堆上分配内存。</p><p><strong>特点</strong>：</p><ul><li>线程共享，是 JVM 管理内存中最大的一块</li><li>垃圾收集器（GC）主要管理的就是堆内存</li><li>分为年轻代、老年代、永久代（Java 8+ 变为元空间）</li></ul><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    subgraph 堆内存        A["堆 Heap"] --> B["年轻代 Young Generation"]        A --> C["老年代 Old Generation"]        B --> D["Eden 区"]        B --> E["S0 区 Survivor From"]        B --> F["S1 区 Survivor To"]        C --> G["Tenured 区"]    end    style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#e8f5e9</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 堆内存演示：创建大量对象</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HeapDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 不断创建对象，观察内存变化</span></span><br><span class="line">        List&lt;<span class="type">byte</span>[]&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="comment">// 每个 byte 数组约 1MB</span></span><br><span class="line">            list.add(<span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span> * <span class="number">1024</span>]);</span><br><span class="line">            System.out.println(<span class="string">&quot;已分配：&quot;</span> + list.size() + <span class="string">&quot; MB&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-方法区（Method-Area）"><a href="#5-方法区（Method-Area）" class="headerlink" title="5. 方法区（Method Area）"></a>5. 方法区（Method Area）</h4><p><strong>作用</strong>：存储类信息（类的字节码、版本号）、常量、静态变量、即时编译器编译后的代码等。</p><p><strong>特点</strong>：</p><ul><li>线程共享</li><li>在 HotSpot JVM 中，Java 8 之前用永久代实现，Java 8 之后用元空间（Metaspace）实现</li><li>元空间使用本地内存，不受 JVM 堆大小限制</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MethodAreaDemo</span> &#123;</span><br><span class="line">    <span class="comment">// 静态变量存储在方法区</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">staticVar</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 常量存储在方法区的常量池</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CONSTANT</span> <span class="operator">=</span> <span class="string">&quot;Hello&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="comment">// 静态代码块也在方法区</span></span><br><span class="line">        System.out.println(<span class="string">&quot;类加载时执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 普通方法不在方法区，具体在虚拟机栈的栈帧中</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="内存溢出示例"><a href="#内存溢出示例" class="headerlink" title="内存溢出示例"></a>内存溢出示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OOM 示例：常量池溢出（Java 7 之前会发生）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OOMDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 使用 String.intern() 将字符串放入常量池</span></span><br><span class="line">        <span class="comment">// 如果不断添加字符串到常量池，最终会 OOM</span></span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            list.add(String.valueOf(i++).intern());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="垃圾回收机制"><a href="#垃圾回收机制" class="headerlink" title="垃圾回收机制"></a>垃圾回收机制</h2><h3 id="什么是垃圾回收？"><a href="#什么是垃圾回收？" class="headerlink" title="什么是垃圾回收？"></a>什么是垃圾回收？</h3><p>垃圾回收（Garbage Collection，简称 GC）是 JVM 自动管理内存的机制，<strong>自动回收不再使用的对象</strong>，防止内存泄漏。</p><p><strong>为什么需要 GC？</strong></p><p>在 C&#x2F;C++ 中，内存需要程序员手动分配和释放，容易出现：</p><ul><li><strong>内存泄漏</strong>：分配后忘记释放，内存越来越少</li><li><strong>野指针</strong>：释放后继续使用，导致程序崩溃</li><li><strong>双重释放</strong>：同一块内存释放两次</li></ul><p>Java 通过 GC 解决了这些问题，程序员只需要关心对象的创建，不用关心释放。</p><h3 id="判断对象是否可回收"><a href="#判断对象是否可回收" class="headerlink" title="判断对象是否可回收"></a>判断对象是否可回收</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["判断对象是否可回收"] --> B["引用计数法"]    A --> C["可达性分析"]    B --> B1["每个对象一个引用计数器"]    B1 --> B2["有引用+1，删除-1"]    B2 --> B3["计数器为0可回收"]    B3 --> B4["无法处理循环引用"]    C --> C1["GC Roots"]    C1 --> C2["从 GC Roots 向下搜索"]    C2 --> C3["走过的路径叫引用链"]    C3 --> C4["不在引用链上=可回收"]    style A fill:#fff3e0</pre></div><h4 id="1-引用计数法"><a href="#1-引用计数法" class="headerlink" title="1. 引用计数法"></a>1. 引用计数法</h4><p>给对象添加一个引用计数器，每当有一个地方引用它时，计数器加 1；引用失效时，计数器减 1。任何时刻计数器为 0 的对象就是不可再使用的。</p><p><strong>优点</strong>：实现简单，判定效率高。</p><p><strong>缺点</strong>：无法解决循环引用问题。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 循环引用示例：引用计数法无法处理</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CircleReference</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> CircleReference ref;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">CircleReference</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CircleReference</span>();  <span class="comment">// a 引用计数 = 1</span></span><br><span class="line">        <span class="type">CircleReference</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CircleReference</span>();  <span class="comment">// b 引用计数 = 1</span></span><br><span class="line"></span><br><span class="line">        a.ref = b;  <span class="comment">// b 引用计数 = 2</span></span><br><span class="line">        b.ref = a;  <span class="comment">// a 引用计数 = 2</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 此时 a 和 b 都不再被其他对象引用（除了互相引用）</span></span><br><span class="line">        <span class="comment">// 引用计数都是 2，但实际上已经&quot;死&quot;了</span></span><br><span class="line"></span><br><span class="line">        a = <span class="literal">null</span>;  <span class="comment">// a 引用计数 = 1</span></span><br><span class="line">        b = <span class="literal">null</span>;  <span class="comment">// b 引用计数 = 1</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 虽然 a 和 b 已经不可达，但引用计数不为 0</span></span><br><span class="line">        <span class="comment">// 引用计数法无法回收这对对象！</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-可达性分析（JVM-使用）"><a href="#2-可达性分析（JVM-使用）" class="headerlink" title="2. 可达性分析（JVM 使用）"></a>2. 可达性分析（JVM 使用）</h4><p>通过一系列称为 “GC Roots” 的对象作为起始点，从这些节点向下搜索，搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时，说明这个对象是不可达的，可以被回收。</p><p><strong>GC Roots 包括</strong>：</p><ul><li>虚拟机栈（栈帧中的本地变量表）中引用的对象</li><li>方法区中类静态属性引用的对象</li><li>方法区中常量引用的对象</li><li>本地方法栈中 JNI（Native 方法）引用的对象</li><li>Java 虚拟机内部的引用（Class 对象、异常对象等）</li><li>所有被同步锁（synchronized）持有的对象</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GC</span> RootsDemo &#123;</span><br><span class="line">    <span class="comment">// GC Roots 1：类的静态属性引用的对象</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> GCRootsDemo instance;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// GC Roots 2：虚拟机栈中引用的对象</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">byte</span>[] data;  <span class="comment">// 占较大内存，避免被优化</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 局部变量表中的引用，作为 GC Roots</span></span><br><span class="line">        <span class="type">GCRootsDemo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GCRootsDemo</span>();  <span class="comment">// demo 是 GC Root</span></span><br><span class="line">        demo.data = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>];  <span class="comment">// 10MB</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// demo 仍然被 instance 引用，不能回收</span></span><br><span class="line">        instance = demo;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 解除 demo 引用</span></span><br><span class="line">        demo = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 此时 demo 对象只被 instance 引用</span></span><br><span class="line">        <span class="comment">// 仍然是可达的，不能被回收</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="垃圾收集算法"><a href="#垃圾收集算法" class="headerlink" title="垃圾收集算法"></a>垃圾收集算法</h3><h4 id="1-标记-清除算法（Mark-Sweep）"><a href="#1-标记-清除算法（Mark-Sweep）" class="headerlink" title="1. 标记-清除算法（Mark-Sweep）"></a>1. 标记-清除算法（Mark-Sweep）</h4><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    subgraph 标记-清除算法        A["初始状态"] --> B["标记阶段"]        B --> C["清除阶段"]        C --> D["回收后"]    end    style A fill:#e3f2fd    style D fill:#e8f5e9</pre></div><p><strong>步骤</strong>：</p><ol><li><strong>标记</strong>：遍历所有对象，标记出所有需要回收的对象</li><li><strong>清除</strong>：统一回收所有被标记的对象</li></ol><p><strong>缺点</strong>：</p><ul><li>效率不高</li><li>会产生大量内存碎片</li></ul><h4 id="2-复制算法（Copying）"><a href="#2-复制算法（Copying）" class="headerlink" title="2. 复制算法（Copying）"></a>2. 复制算法（Copying）</h4><p>将可用内存按容量划分为大小相等的两块，每次只使用其中一块。当这一块内存用完了，就将还存活的对象复制到另一块上面，然后再把已使用过的内存空间一次清理掉。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["内存区域"] --> B["From 区 使用中"]    A --> C["To 区 空闲"]    B -->|存活对象复制| C    B -->|清空| D["From 区清空"]    C -->|交换角色| E["To 变 From"]    D -->|交换角色| F["From 变 To"]</pre></div><p><strong>优点</strong>：实现简单，运行高效，不会产生内存碎片。</p><p><strong>缺点</strong>：可用内存缩小为原来的一半，浪费空间。</p><p><strong>应用</strong>：年轻代使用此算法，因为年轻代对象存活率低。</p><h4 id="3-标记-整理算法（Mark-Compact）"><a href="#3-标记-整理算法（Mark-Compact）" class="headerlink" title="3. 标记-整理算法（Mark-Compact）"></a>3. 标记-整理算法（Mark-Compact）</h4><p>针对老年代的特点，在标记-清除算法的基础上增加了”整理”步骤。标记过程与标记-清除算法一样，但后续不是直接清理可回收对象，而是让所有存活的对象都向一端移动，然后直接清理掉边界以外的内存。</p><p><strong>优点</strong>：不会产生内存碎片。</p><p><strong>缺点</strong>：整理过程需要移动对象，效率较低。</p><h4 id="4-分代收集算法（Generational-Collection）"><a href="#4-分代收集算法（Generational-Collection）" class="headerlink" title="4. 分代收集算法（Generational Collection）"></a>4. 分代收集算法（Generational Collection）</h4><p>当前商业 JVM 普遍采用这种算法。根据对象存活周期的不同将内存划分为几块，一般把 Java 堆分为年轻代和老年代，然后根据各年代的特点采用最适当的收集算法。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["分代收集算法"] --> B["年轻代"]    A --> C["老年代"]    B --> B1[" Eden 区"]    B --> B2["Survivor 区"]    C --> C1["标记-整理算法"]    B1 --> B3["复制算法"]    B2 --> B3    style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#e8f5e9</pre></div><h3 id="垃圾收集器"><a href="#垃圾收集器" class="headerlink" title="垃圾收集器"></a>垃圾收集器</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    subgraph 垃圾收集器        A["年轻代收集器"] --> B1["Serial"]        A --> B2["ParNew"]        A --> B3["Parallel Scavenge"]        A --> B4["G1"]        A --> B5["ZGC"]        A --> B6["Shenandoah"]        C["老年代收集器"] --> C1["Serial Old"]        C --> C2["Parallel Old"]        C --> C3["CMS"]        C --> C4["G1"]        C --> C5["ZGC"]        C --> C6["Shenandoah"]    end</pre></div><h4 id="Serial-收集器"><a href="#Serial-收集器" class="headerlink" title="Serial 收集器"></a>Serial 收集器</h4><p>最基本、历史最悠久的收集器。是一个单线程收集器，在进行垃圾收集时，必须暂停所有其他工作线程（Stop The World）。</p><p><strong>特点</strong>：简单高效，Client 模式下默认的年轻代收集器。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定使用 Serial 收集器</span></span><br><span class="line">-XX:+UseSerialGC</span><br></pre></td></tr></table></figure><h4 id="ParNew-收集器"><a href="#ParNew-收集器" class="headerlink" title="ParNew 收集器"></a>ParNew 收集器</h4><p>Serial 收集器的多线程版本，除了使用多线程进行垃圾收集外，其余行为与 Serial 完全一样。</p><p><strong>特点</strong>：Server 模式下首选的年轻代收集器，可以与 CMS 配合使用。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定使用 ParNew 收集器</span></span><br><span class="line">-XX:+UseParNewGC</span><br></pre></td></tr></table></figure><h4 id="Parallel-Scavenge-收集器"><a href="#Parallel-Scavenge-收集器" class="headerlink" title="Parallel Scavenge 收集器"></a>Parallel Scavenge 收集器</h4><p>关注点是吞吐量（Throughput），即吞吐量 &#x3D; 运行用户代码时间 &#x2F; (运行用户代码时间 + 垃圾收集时间)。</p><p><strong>特点</strong>：自适应调节策略，GC 自适应调节参数。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定使用 Parallel Scavenge 收集器</span></span><br><span class="line">-XX:+UseParallelGC</span><br><span class="line"><span class="comment"># 设置最大 GC 暂停时间</span></span><br><span class="line">-XX:MaxGCPauseMillis=100</span><br><span class="line"><span class="comment"># 设置吞吐量大小</span></span><br><span class="line">-XX:GCTimeRatio=99</span><br></pre></td></tr></table></figure><h4 id="CMS-收集器"><a href="#CMS-收集器" class="headerlink" title="CMS 收集器"></a>CMS 收集器</h4><p>Concurrent Mark Sweep，以获取最短回收停顿时间为目标的收集器。</p><p><strong>收集过程</strong>：</p><ol><li>初始标记（Initial Mark）：标记 GC Roots 能直接关联到的对象</li><li>并发标记（Concurrent Mark）：进行 GC Roots Tracing</li><li>重新标记（Remark）：修正并发标记期间因用户程序继续运作而导致标记产生变动</li><li>并发清除（Concurrent Sweep）：清理垃圾</li></ol><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>sequenceDiagram    participant STW as Stop The World    participant GC as CMS GC        GC->>STW: 1. 初始标记（STW）    Note over STW: 标记 GC Roots 直接关联对象    STW-->>GC: 完成        GC->>GC: 2. 并发标记（并发）    Note over GC: 遍历引用链，标记所有存活对象        GC->>STW: 3. 重新标记（STW）    Note over STW: 修正并发标记期间变动的对象    STW-->>GC: 完成        GC->>GC: 4. 并发清除（并发）    Note over GC: 清除已死亡的对象</pre></div><p><strong>缺点</strong>：</p><ul><li>对 CPU 资源敏感，会占用 CPU 资源</li><li>无法处理浮动垃圾（并发清理阶段产生的垃圾）</li><li>产生内存碎片</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定使用 CMS 收集器</span></span><br><span class="line">-XX:+UseConcMarkSweepGC</span><br><span class="line"><span class="comment"># 触发 CMS 的阈值</span></span><br><span class="line">-XX:CMSInitiatingOccupancyFraction=68</span><br></pre></td></tr></table></figure><h4 id="G1-收集器"><a href="#G1-收集器" class="headerlink" title="G1 收集器"></a>G1 收集器</h4><p>Garbage First，将整个堆划分为多个大小相等的 Region，跟踪各个 Region 里面的垃圾堆积价值（回收所获得的空间大小以及回收所需时间的经验值），在后台维护一个优先列表，每次根据允许的收集时间，优先回收价值最大的 Region。</p><p><strong>特点</strong>：</p><ul><li>并行与并发</li><li>分代收集</li><li>空间整合（不产生内存碎片）</li><li>可预测的停顿</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定使用 G1 收集器</span></span><br><span class="line">-XX:+UseG1GC</span><br><span class="line"><span class="comment"># 设置目标停顿时间</span></span><br><span class="line">-XX:MaxGCPauseMillis=200</span><br><span class="line"><span class="comment"># 设置 Region 大小</span></span><br><span class="line">-XX:G1HeapRegionSize=mb</span><br></pre></td></tr></table></figure><hr><h2 id="类加载机制"><a href="#类加载机制" class="headerlink" title="类加载机制"></a>类加载机制</h2><h3 id="类的生命周期"><a href="#类的生命周期" class="headerlink" title="类的生命周期"></a>类的生命周期</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["类的生命周期"] --> B["加载 Loading"]    B --> C["验证 Verification"]    C --> D["准备 Preparation"]    D --> E["解析 Resolution"]    E --> F["初始化 Initialization"]    F --> G["使用 Using"]    G --> H["卸载 Unloading"]    style A fill:#fff3e0</pre></div><h3 id="类加载的过程"><a href="#类加载的过程" class="headerlink" title="类加载的过程"></a>类加载的过程</h3><h4 id="1-加载（Loading）"><a href="#1-加载（Loading）" class="headerlink" title="1. 加载（Loading）"></a>1. 加载（Loading）</h4><p>“加载”是”类加载”过程的一个阶段，两者不可混淆。</p><p><strong>此阶段完成的事情</strong>：</p><ul><li>通过类的全限定名获取定义此类的二进制字节流</li><li>将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构</li><li>在内存中生成一个代表这个类的 java.lang.Class 对象，作为方法区这个类的各种数据的访问入口</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 类的加载：Class 对象是访问类元数据的入口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoadingDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 获取 Class 对象的方式</span></span><br><span class="line">        <span class="comment">// 方式一：Class.forName()</span></span><br><span class="line">        Class&lt;?&gt; clazz1 = Class.forName(<span class="string">&quot;java.lang.String&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式二：.class 属性</span></span><br><span class="line">        Class&lt;?&gt; clazz2 = String.class;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式三：对象的 getClass() 方法</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">        Class&lt;?&gt; clazz3 = str.getClass();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 三个 Class 对象是同一个（类的 Class 对象在 JVM 中唯一）</span></span><br><span class="line">        System.out.println(clazz1 == clazz2);  <span class="comment">// true</span></span><br><span class="line">        System.out.println(clazz2 == clazz3);  <span class="comment">// true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-验证（Verification）"><a href="#2-验证（Verification）" class="headerlink" title="2. 验证（Verification）"></a>2. 验证（Verification）</h4><p>确保 Class 文件的字节流中包含的信息符合当前 JVM 要求，不会危害 JVM 安全。</p><p><strong>验证阶段包括</strong>：</p><ul><li>文件格式验证：验证字节流是否符合 Class 文件格式规范</li><li>元数据验证：对字节码描述的信息进行语义分析</li><li>字节码验证：通过数据流和控制流分析，确保程序语义的合法性</li><li>符号引用验证：确保解析动作能正常执行</li></ul><h4 id="3-准备（Preparation）"><a href="#3-准备（Preparation）" class="headerlink" title="3. 准备（Preparation）"></a>3. 准备（Preparation）</h4><p><strong>正式为类变量分配内存并设置类变量初始值的阶段</strong>，这些变量所使用的内存都将在方法区中进行分配。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PreparationDemo</span> &#123;</span><br><span class="line">    <span class="comment">// 类变量：准备阶段赋默认值</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">staticVar</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 常量：准备阶段赋最终值</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">CONSTANT</span> <span class="operator">=</span> <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 编译时常量在准备阶段就被替换为具体值</span></span><br><span class="line">    <span class="comment">// 因为它是 static final，编译器就知道值是多少</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：</p><ul><li>这时候进行内存分配的仅包括类变量（static 修饰的变量），不包括实例变量</li><li>实例变量会在对象实例化时随对象一起分配在堆中</li><li>初始值通常是零值，而非代码中赋予的值</li></ul><table><thead><tr><th>类型</th><th>零值</th></tr></thead><tbody><tr><td>int</td><td>0</td></tr><tr><td>long</td><td>0L</td></tr><tr><td>short</td><td>(short) 0</td></tr><tr><td>byte</td><td>(byte) 0</td></tr><tr><td>char</td><td>‘\u0000’</td></tr><tr><td>boolean</td><td>false</td></tr><tr><td>reference</td><td>null</td></tr><tr><td>float</td><td>0.0f</td></tr><tr><td>double</td><td>0.0d</td></tr></tbody></table><h4 id="4-解析（Resolution）"><a href="#4-解析（Resolution）" class="headerlink" title="4. 解析（Resolution）"></a>4. 解析（Resolution）</h4><p><strong>将常量池内的符号引用替换为直接引用的过程</strong>。</p><ul><li><strong>符号引用</strong>：以一组符号来描述所引用的目标，符号可以是任何形式的字面量</li><li><strong>直接引用</strong>：直接指向目标的指针、相对偏移量或能间接定位到目标的句柄</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 解析示例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResolutionDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;java.lang.String&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 获取方法或字段时，JVM 会进行解析</span></span><br><span class="line">        <span class="comment">// 符号引用 -&gt; 直接引用</span></span><br><span class="line">        java.lang.reflect.<span class="type">Field</span> <span class="variable">field</span> <span class="operator">=</span> </span><br><span class="line">            clazz.getDeclaredField(<span class="string">&quot;value&quot;</span>);</span><br><span class="line">        <span class="comment">// field 是一个直接引用，指向 actual character array</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-初始化（Initialization）"><a href="#5-初始化（Initialization）" class="headerlink" title="5. 初始化（Initialization）"></a>5. 初始化（Initialization）</h4><p><strong>类加载过程的最后一步</strong>，真正执行类中定义的 Java 代码。</p><p><strong>触发初始化的场景（主动引用）</strong>：</p><ol><li>遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时</li><li>使用 java.lang.reflect 包的方法对类进行反射调用时</li><li>当初始化一个类时，发现其父类还没初始化，需要先触发父类初始化</li><li>JVM 启动时，会先初始化包含 main() 方法的那个类</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InitializationDemo</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;InitializationDemo 被初始化！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 触发 InitializationDemo 初始化</span></span><br><span class="line">        System.out.println(<span class="string">&quot;main 方法执行&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 触发 A 初始化</span></span><br><span class="line">        <span class="comment">// A a = new A();  // 如果有 A 类，会触发其初始化</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;A 被初始化！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="类加载器"><a href="#类加载器" class="headerlink" title="类加载器"></a>类加载器</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["类加载器"] --> B["Bootstrap ClassLoader 启动类加载器"]    A --> C["Extension ClassLoader 扩展类加载器"]    A --> D["Application ClassLoader 应用类加载器"]    B --> C    C --> D    B -.->|最顶层| E["C++ 实现"]    C -.->|Java 实现| F["ExtClassLoader"]    D -.->|Java 实现| G["AppClassLoader"]    style A fill:#fff3e0</pre></div><h4 id="三种默认类加载器"><a href="#三种默认类加载器" class="headerlink" title="三种默认类加载器"></a>三种默认类加载器</h4><ol><li><p><strong>Bootstrap ClassLoader（启动类加载器）</strong></p><ul><li>最顶层的类加载器</li><li>由 C++ 实现，负责加载 <code>JAVA_HOME/lib</code> 目录中的类库</li><li>无法被 Java 程序直接引用</li></ul></li><li><p><strong>Extension ClassLoader（扩展类加载器）</strong></p><ul><li>由 Java 实现（<code>ExtClassLoader</code>）</li><li>负责加载 <code>JAVA_HOME/lib/ext</code> 目录中的类库</li><li>开发者可以把自己写的类打包成 jar 放到此目录</li></ul></li><li><p><strong>Application ClassLoader（应用类加载器）</strong></p><ul><li>由 Java 实现（<code>AppClassLoader</code>）</li><li>负责加载 classpath 上指定的类库</li><li>一般情况下这是程序中默认的类加载器</li></ul></li></ol><h4 id="双亲委派模型"><a href="#双亲委派模型" class="headerlink" title="双亲委派模型"></a>双亲委派模型</h4><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["Application ClassLoader"] --> B["Extension ClassLoader"]    B --> C["Bootstrap ClassLoader"]    C -->|找不到类| D["向下查找"]    D --> E["Extension ClassLoader"]    E -->|找不到类| F["向下查找"]    F --> G["Application ClassLoader"]    G -->|找不到类| H["抛出 ClassNotFoundException"]    style A fill:#e3f2fd    style C fill:#fff3e0</pre></div><p><strong>工作过程</strong>：如果一个类加载器收到了类加载的请求，它首先不会自己去尝试加载这个类，而是把这个请求委派给父类加载器去完成，每一层都是如此，因此所有的加载请求最终都应该传送到顶层的启动类加载器中，只有当父加载器反馈自己无法完成这个加载请求时，子加载器才会尝试自己去加载。</p><p><strong>为什么需要双亲委派模型？</strong></p><ul><li><strong>安全性</strong>：防止核心类被篡改。比如用户自己写一个 <code>java.lang.String</code> 类，由于双亲委派模型，这个类永远不会被加载，因为会优先使用父加载器加载的 <code>java.lang.String</code>。</li><li><strong>避免重复加载</strong>：父加载器已加载的类，子加载器不需要再次加载。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类加载器示例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClassLoader</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String classPath;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyClassLoader</span><span class="params">(String classPath)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.classPath = classPath;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        <span class="type">byte</span>[] classData = loadClassData(name);</span><br><span class="line">        <span class="keyword">if</span> (classData == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ClassNotFoundException</span>(name);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> defineClass(name, classData, <span class="number">0</span>, classData.length);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">byte</span>[] loadClassData(String className) &#123;</span><br><span class="line">        <span class="comment">// 从文件系统读取 class 文件</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> classPath + File.separatorChar </span><br><span class="line">            + className.replace(<span class="string">&#x27;.&#x27;</span>, File.separatorChar) + <span class="string">&quot;.class&quot;</span>;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(fileName);</span><br><span class="line">             <span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>()) &#123;</span><br><span class="line">            <span class="type">int</span> data;</span><br><span class="line">            <span class="keyword">while</span> ((data = is.read()) != -<span class="number">1</span>) &#123;</span><br><span class="line">                baos.write(data);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> baos.toByteArray();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="并发编程"><a href="#并发编程" class="headerlink" title="并发编程"></a>并发编程</h2><h3 id="为什么要并发？"><a href="#为什么要并发？" class="headerlink" title="为什么要并发？"></a>为什么要并发？</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["单线程"] --> B["CPU 空闲 等待 I/O"]    A -->|I/O 操作| C["阻塞"]    D["多线程"] --> E["线程1 等待 I/O"]    D --> F["线程2 处理业务"]    style B fill:#ffcdd2    style E fill:#fff9c4    style F fill:#c8e6c9</pre></div><p><strong>单线程的问题</strong>：</p><ul><li>多核 CPU 环境下，只用一个核太浪费</li><li>I&#x2F;O 等待时 CPU 空闲，效率低</li></ul><p><strong>多线程的优势</strong>：</p><ul><li>提高 CPU 利用率</li><li>提高程序吞吐量</li><li>便于异步处理</li><li>提升用户体验</li></ul><h3 id="并发编程三大概念"><a href="#并发编程三大概念" class="headerlink" title="并发编程三大概念"></a>并发编程三大概念</h3><h4 id="1-原子性（Atomicity）"><a href="#1-原子性（Atomicity）" class="headerlink" title="1. 原子性（Atomicity）"></a>1. 原子性（Atomicity）</h4><p>一个操作或多个操作要么全部执行，要么全部不执行。</p><p><strong>示例</strong>：i++ 不是原子操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AtomicityDemo</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++) &#123;</span><br><span class="line">            count++;  <span class="comment">// 这不是原子操作！</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">AtomicityDemo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicityDemo</span>();</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(demo);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(demo);</span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">        t1.join();</span><br><span class="line">        t2.join();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 期望：20000，实际可能小于 20000</span></span><br><span class="line">        System.out.println(<span class="string">&quot;最终结果：&quot;</span> + demo.count);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>count++</code> 实际上分为三步：</p><ol><li>读取 count 的值</li><li>将 count 加 1</li><li>写入新的 count 值</li></ol><p>多线程下可能导致数据不一致。</p><p><strong>解决方案</strong>：使用 AtomicInteger</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.atomic.AtomicInteger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AtomicDemo</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++) &#123;</span><br><span class="line">            count.incrementAndGet();  <span class="comment">// 原子操作</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getCount</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> count.get();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-可见性（Visibility）"><a href="#2-可见性（Visibility）" class="headerlink" title="2. 可见性（Visibility）"></a>2. 可见性（Visibility）</h4><p>一个线程对共享变量的修改，其他线程能够立即看到。</p><p><strong>示例</strong>：可见性问题</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VisibilityDemo</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line">    <span class="comment">// 加上 volatile 关键字</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">running</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (running) &#123;</span><br><span class="line">            <span class="comment">// 无限循环</span></span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;线程结束了&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">stopRunning</span><span class="params">()</span> &#123;</span><br><span class="line">        running = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">VisibilityDemo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VisibilityDemo</span>();</span><br><span class="line">        demo.start();</span><br><span class="line">        </span><br><span class="line">        Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        demo.stopRunning();  <span class="comment">// 尝试停止线程</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 如果没有 volatile，主线程修改 running 后</span></span><br><span class="line">        <span class="comment">// 子线程可能看不到（缓存问题），导致无法停止</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-有序性（Ordering）"><a href="#3-有序性（Ordering）" class="headerlink" title="3. 有序性（Ordering）"></a>3. 有序性（Ordering）</h4><p>程序执行的顺序按照代码的先后顺序执行。</p><p><strong>问题</strong>：编译器和处理器可能会对指令进行重排序。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 有序性问题示例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderingDemo</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">writer</span><span class="params">()</span> &#123;</span><br><span class="line">        a = <span class="number">1</span>;          <span class="comment">// 1. 给 a 赋值</span></span><br><span class="line">        flag = <span class="literal">true</span>;    <span class="comment">// 2. 设置 flag</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">reader</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (flag) &#123;     <span class="comment">// 3. 读取 flag</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> a;  <span class="comment">// 4. 读取 a</span></span><br><span class="line">            System.out.println(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>指令重排后可能的执行顺序：</p><ol><li>flag &#x3D; true</li><li>a &#x3D; 1</li><li>读取 a（此时 a &#x3D; 1）</li><li>读取 flag（此时 flag &#x3D; true）</li></ol><p>导致 i 的值可能不是我们期望的 1。</p><h3 id="volatile-关键字"><a href="#volatile-关键字" class="headerlink" title="volatile 关键字"></a>volatile 关键字</h3><p><code>volatile</code> 是 Java 中最轻量的同步机制，它有两个作用：</p><ol><li><strong>保证可见性</strong>：被 volatile 修饰的变量，修改后会立即写回主内存，并让其他线程的缓存失效</li><li><strong>禁止指令重排序</strong>：volatile 变量操作前后的代码不能被重排序</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileDemo</span> &#123;</span><br><span class="line">    <span class="comment">// volatile 保证：每次读取都从主内存读，写完立即刷新到主内存</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">increment</span><span class="params">()</span> &#123;</span><br><span class="line">        count++;  <span class="comment">// 不是原子操作，但 volatile 保证可见性</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>volatile 的适用场景</strong>：</p><ul><li>状态标志位（如 running）</li><li>双重检查锁定（Double Checked Locking）</li><li>观察量（observer）</li></ul><p><strong>volatile 的局限性</strong>：</p><ul><li>不能保证原子性（如 count++）</li><li>不能替代 synchronized</li></ul><h3 id="synchronized-关键字"><a href="#synchronized-关键字" class="headerlink" title="synchronized 关键字"></a>synchronized 关键字</h3><p><code>synchronized</code> 是 Java 中最基础的同步机制，可以保证：</p><ul><li>原子性</li><li>可见性</li><li>有序性</li></ul><p><strong>用法</strong>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SynchronizedDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1. 修饰实例方法 - 锁对象本身</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 同一时刻只有一个线程能执行这个方法</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. 修饰静态方法 - 锁类对象</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">method2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 同一时刻只有一个线程能执行这个方法</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. 修饰代码块 - 锁指定对象</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method3</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;  <span class="comment">// 锁当前对象</span></span><br><span class="line">            <span class="comment">// 同步代码</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method4</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (SynchronizedDemo.class) &#123;  <span class="comment">// 锁类对象</span></span><br><span class="line">            <span class="comment">// 同步代码</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Synchronized 原理</strong>：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["进入 synchronized"] --> B{对象是否被占用}    B -->|否| C["获取锁 设置 Mark Word"]    B -->|是| D{是否同一线程}    D -->|是| E["重入 计数器+1"]    D -->|否| F["进入等待队列 阻塞"]    C --> G["执行同步代码"]    E --> G    G --> H{正常退出 异常退出}    H --> I["释放锁 计数器-1"]    I -->|计数器=0| J["唤醒等待线程"]    style C fill:#c8e6c9    style F fill:#ffcdd2</pre></div><h3 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h3><p>线程池是并发编程中最重要的组件之一，它避免了频繁创建和销毁线程的开销。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["提交任务"] --> B{核心线程池 是否满}    B -->|否| C["创建新线程 执行任务"]    B -->|是| D{任务队列 是否满}    D -->|否| E["任务入队等待"]    D -->|是| F{最大线程数 是否满}    F -->|否| G["创建新线程 执行任务"]    F -->|是| H["执行拒绝策略"]    C --> I["任务执行完成"]    E --> I    G --> I    style A fill:#e3f2fd    style H fill:#ffcdd2</pre></div><h4 id="线程池参数"><a href="#线程池参数" class="headerlink" title="线程池参数"></a>线程池参数</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 七大参数</span></span><br><span class="line">        <span class="type">ThreadPoolExecutor</span> <span class="variable">executor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(</span><br><span class="line">            <span class="number">2</span>,                      <span class="comment">// corePoolSize：核心线程数</span></span><br><span class="line">            <span class="number">5</span>,                      <span class="comment">// maximumPoolSize：最大线程数</span></span><br><span class="line">            <span class="number">60L</span>,                    <span class="comment">// keepAliveTime：空闲线程存活时间</span></span><br><span class="line">            TimeUnit.SECONDS,       <span class="comment">// 时间单位</span></span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;&gt;(<span class="number">3</span>),  <span class="comment">// workQueue：任务队列</span></span><br><span class="line">            Executors.defaultThreadFactory(),  <span class="comment">// threadFactory：线程工厂</span></span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.AbortPolicy()  <span class="comment">// handler：拒绝策略</span></span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交 8 个任务测试</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">8</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">taskId</span> <span class="operator">=</span> i;</span><br><span class="line">            executor.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;任务 &quot;</span> + taskId + <span class="string">&quot; 由线程 &quot;</span></span><br><span class="line">                    + Thread.currentThread().getName() + <span class="string">&quot; 执行&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        executor.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="拒绝策略"><a href="#拒绝策略" class="headerlink" title="拒绝策略"></a>拒绝策略</h4><table><thead><tr><th>策略</th><th>行为</th></tr></thead><tbody><tr><td>AbortPolicy</td><td>抛出 RejectedExecutionException（默认）</td></tr><tr><td>CallerRunsPolicy</td><td>由调用者线程执行</td></tr><tr><td>DiscardPolicy</td><td>静默丢弃任务</td></tr><tr><td>DiscardOldestPolicy</td><td>丢弃队列中最老的任务</td></tr></tbody></table><h4 id="Executors-工具类创建线程池"><a href="#Executors-工具类创建线程池" class="headerlink" title="Executors 工具类创建线程池"></a>Executors 工具类创建线程池</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 固定线程数线程池（适合CPU密集型）</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">fixedPool</span> <span class="operator">=</span> </span><br><span class="line">            Executors.newFixedThreadPool(<span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 2. 单线程线程池（保证顺序执行）</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">singlePool</span> <span class="operator">=</span> </span><br><span class="line">            Executors.newSingleThreadExecutor();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3. 缓存线程池（适合IO密集型，任务多但执行快）</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">cachedPool</span> <span class="operator">=</span> </span><br><span class="line">            Executors.newCachedThreadPool();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 4. 定时线程池</span></span><br><span class="line">        <span class="type">ScheduledExecutorService</span> <span class="variable">scheduledPool</span> <span class="operator">=</span> </span><br><span class="line">            Executors.newScheduledThreadPool(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 5. 工作窃取线程池</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">workStealingPool</span> <span class="operator">=</span> </span><br><span class="line">            Executors.newWorkStealingPool();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 注意：实际生产中推荐使用 ThreadPoolExecutor</span></span><br><span class="line">        <span class="comment">// Executors 创建的线程池可能有 OOM 风险</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="JUC-并发工具类"><a href="#JUC-并发工具类" class="headerlink" title="JUC 并发工具类"></a>JUC 并发工具类</h3><h4 id="CountDownLatch"><a href="#CountDownLatch" class="headerlink" title="CountDownLatch"></a>CountDownLatch</h4><p>门闩，等待一组线程完成后再执行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">latch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">3</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">playerId</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;选手 &quot;</span> + playerId + <span class="string">&quot; 准备完成&quot;</span>);</span><br><span class="line">                latch.countDown();  <span class="comment">// 完成一项准备</span></span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待所有选手准备完成</span></span><br><span class="line">        latch.await();</span><br><span class="line">        System.out.println(<span class="string">&quot;所有选手准备完毕，开始比赛！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="CyclicBarrier"><a href="#CyclicBarrier" class="headerlink" title="CyclicBarrier"></a>CyclicBarrier</h4><p>循环栅栏，让一组线程互相等待，达到某个点后全部放行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">CyclicBarrier</span> <span class="variable">barrier</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(<span class="number">3</span>, () -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;所有玩家都准备好了，游戏开始！&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">3</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">playerId</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;玩家 &quot;</span> + playerId + <span class="string">&quot; 加载中...&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep((<span class="type">long</span>) (Math.random() * <span class="number">1000</span>));</span><br><span class="line">                    System.out.println(<span class="string">&quot;玩家 &quot;</span> + playerId + <span class="string">&quot; 准备完成，等待其他人...&quot;</span>);</span><br><span class="line">                    barrier.await();  <span class="comment">// 等待其他玩家</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException | BrokenBarrierException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Semaphore"><a href="#Semaphore" class="headerlink" title="Semaphore"></a>Semaphore</h4><p>信号量，控制同时访问某个资源的线程数量。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SemaphoreDemo</span> &#123;</span><br><span class="line">    <span class="comment">// 模拟停车位，只有 3 个</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">SPOTS</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Semaphore</span> <span class="variable">semaphore</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Semaphore</span>(SPOTS);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">5</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">carId</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    semaphore.acquire();  <span class="comment">// 获取许可证</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;车辆 &quot;</span> + carId + <span class="string">&quot; 进入停车场&quot;</span>);</span><br><span class="line">                    Thread.sleep((<span class="type">long</span>) (Math.random() * <span class="number">3000</span>));</span><br><span class="line">                    System.out.println(<span class="string">&quot;车辆 &quot;</span> + carId + <span class="string">&quot; 离开停车场&quot;</span>);</span><br><span class="line">                    semaphore.release();  <span class="comment">// 释放许可证</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h4><p>线程安全的 HashMap，高并发场景首选。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMapDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        ConcurrentHashMap&lt;String, Integer&gt; map = </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 常用操作</span></span><br><span class="line">        map.put(<span class="string">&quot;A&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        map.putIfAbsent(<span class="string">&quot;B&quot;</span>, <span class="number">2</span>);  <span class="comment">// 不存在才插入</span></span><br><span class="line">        map.get(<span class="string">&quot;A&quot;</span>);</span><br><span class="line">        map.remove(<span class="string">&quot;A&quot;</span>);</span><br><span class="line">        map.size();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 原子操作</span></span><br><span class="line">        map.putIfAbsent(<span class="string">&quot;counter&quot;</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 原子递增</span></span><br><span class="line">        map.replace(<span class="string">&quot;counter&quot;</span>, map.get(<span class="string">&quot;counter&quot;</span>), </span><br><span class="line">            map.get(<span class="string">&quot;counter&quot;</span>) + <span class="number">1</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// Java 8+ 支持 compute 方法</span></span><br><span class="line">        map.compute(<span class="string">&quot;counter&quot;</span>, (k, v) -&gt; v == <span class="literal">null</span> ? <span class="number">1</span> : v + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// computeIfPresent：key 存在才更新</span></span><br><span class="line">        map.computeIfPresent(<span class="string">&quot;counter&quot;</span>, (k, v) -&gt; v + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// computeIfAbsent：key 不存在才添加</span></span><br><span class="line">        map.computeIfAbsent(<span class="string">&quot;newCounter&quot;</span>, k -&gt; <span class="number">10</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="集合源码解析"><a href="#集合源码解析" class="headerlink" title="集合源码解析"></a>集合源码解析</h2><h3 id="HashMap-底层实现"><a href="#HashMap-底层实现" class="headerlink" title="HashMap 底层实现"></a>HashMap 底层实现</h3><p>HashMap 是 Java 中最常用的 Map 实现，了解其源码对于深入理解数据结构至关重要。</p><h4 id="JDK-1-7-vs-JDK-1-8"><a href="#JDK-1-7-vs-JDK-1-8" class="headerlink" title="JDK 1.7 vs JDK 1.8"></a>JDK 1.7 vs JDK 1.8</h4><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["JDK 1.7"] --> B["数组 + 链表 Entry<K,V>[] table"]    A --> C["头插法 可能导致死循环"]    D["JDK 1.8"] --> E["数组 + 链表 + 红黑树 Node<K,V>[] table"]    D --> F["尾插法 避免死循环"]    D --> G["链表长度>8转红黑树"]    style B fill:#e3f2fd    style E fill:#c8e6c9</pre></div><h4 id="HashMap-基本结构"><a href="#HashMap-基本结构" class="headerlink" title="HashMap 基本结构"></a>HashMap 基本结构</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// HashMap 核心属性</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HashMap</span>&lt;K, V&gt; <span class="keyword">extends</span> <span class="title class_">AbstractMap</span>&lt;K, V&gt; </span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">Map</span>&lt;K, V&gt;, Cloneable, Serializable &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 默认初始容量（16）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_INITIAL_CAPACITY</span> <span class="operator">=</span> <span class="number">1</span> &lt;&lt; <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最大容量（2^30）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MAXIMUM_CAPACITY</span> <span class="operator">=</span> <span class="number">1</span> &lt;&lt; <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 负载因子（0.75，元素达到容量的75%时扩容）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">float</span> <span class="variable">DEFAULT_LOAD_FACTOR</span> <span class="operator">=</span> <span class="number">0.75f</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 树化阈值（链表转红黑树）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">TREEIFY_THRESHOLD</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 解除树化阈值（红黑树转链表）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">UNTREEIFY_THRESHOLD</span> <span class="operator">=</span> <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最小树化容量（整个 HashMap 桶数 &gt;= 64 才允许树化）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MIN_TREEIFY_CAPACITY</span> <span class="operator">=</span> <span class="number">64</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 存储数据的数组</span></span><br><span class="line">    <span class="keyword">transient</span> Node&lt;K, V&gt;[] table;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 元素个数</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="type">int</span> size;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="hash-方法详解"><a href="#hash-方法详解" class="headerlink" title="hash() 方法详解"></a>hash() 方法详解</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// HashMap 的 hash 算法</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hash</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">    <span class="type">int</span> h;</span><br><span class="line">    <span class="comment">// 扰动函数：让高位参与运算，减少碰撞</span></span><br><span class="line">    <span class="keyword">return</span> (key == <span class="literal">null</span>) ? <span class="number">0</span> : (h = key.hashCode()) ^ (h &gt;&gt;&gt; <span class="number">16</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["key.hashCode"] --> B["高16位不变 低16位与高16位异或"]    B --> C["让hash分布更均匀"]    C --> D["计算数组下标 hash & (length-1)"]    style A fill:#e3f2fd    style D fill:#c8e6c9</pre></div><p><strong>为什么用 (n-1) &amp; hash 而不是 hash % n？</strong></p><ul><li><code>n</code> 必须是 2 的幂次方（如 16）</li><li><code>n - 1</code> 的二进制是低位全为 1</li><li><code>&amp;</code> 操作比 <code>%</code> 操作快得多</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 计算桶位置</span></span><br><span class="line"><span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hash(key) &amp; (capacity - <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 例如：capacity = 16 = 2^4，capacity - 1 = 15 = 0b1111</span></span><br><span class="line"><span class="comment">// hash(key) = 31 = 0b11111</span></span><br><span class="line"><span class="comment">// index = 0b11111 &amp; 0b1111 = 0b1111 = 15</span></span><br></pre></td></tr></table></figure><h4 id="put-方法流程"><a href="#put-方法流程" class="headerlink" title="put() 方法流程"></a>put() 方法流程</h4><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["put(key, value)"] --> B{key 是否为 null}    B -->|是| C["处理 key 为 null 的情况"]    B -->|否| D["计算 hash(key)"]    D --> E["计算数组下标"]    E --> F{该位置是否为空}    F -->|是| G["直接插入 Node"]    F -->|否| H{该位置是否是红黑树}    H -->|是| I["插入红黑树节点"]    H -->|否| J{链表是否已存在该 key}    J -->|是| K["覆盖 value"]    J -->|否| L["尾插法添加到链表"]    L --> M{链表长度是否>8}    M -->|是| N{是否满足树化条件}    N -->|是| O["链表转为红黑树"]    N -->|否| P["继续使用链表"]    G --> Q["检查是否需要扩容"]    I --> Q    K --> Q    O --> Q    P --> Q    Q --> R{size 是否 > threshold}    R -->|是| S["扩容 resize"]    R -->|否| T["返回"]    style S fill:#ffcdd2</pre></div><h4 id="扩容机制"><a href="#扩容机制" class="headerlink" title="扩容机制"></a>扩容机制</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 扩容方法</span></span><br><span class="line">Node&lt;K, V&gt;[] resize() &#123;</span><br><span class="line">    <span class="comment">// 新容量 = 旧容量 * 2</span></span><br><span class="line">    newCap = oldCap &lt;&lt; <span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 新阈值 = 旧阈值 * 2</span></span><br><span class="line">    newThr = oldThr &lt;&lt; <span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 创建新数组</span></span><br><span class="line">    Node&lt;K, V&gt;[] newTable = <span class="keyword">new</span> <span class="title class_">Node</span>[newCap];</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 迁移元素（重点）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; oldCap; j++) &#123;</span><br><span class="line">        Node&lt;K, V&gt; e;</span><br><span class="line">        <span class="keyword">if</span> ((e = oldTab[j]) != <span class="literal">null</span>) &#123;</span><br><span class="line">            oldTab[j] = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span> (e.next == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 只有一个节点，直接计算新位置</span></span><br><span class="line">                newTab[e.hash &amp; (newCap - <span class="number">1</span>)] = e;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> TreeNode) &#123;</span><br><span class="line">                <span class="comment">// 红黑树拆分</span></span><br><span class="line">                ((TreeNode&lt;K, V&gt;) e).split(<span class="built_in">this</span>, newTable, j, oldCap);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 链表拆分（JDK 1.8 尾插法）</span></span><br><span class="line">                Node&lt;K, V&gt; loHead = <span class="literal">null</span>, loTail = <span class="literal">null</span>;</span><br><span class="line">                Node&lt;K, V&gt; hiHead = <span class="literal">null</span>, hiTail = <span class="literal">null</span>;</span><br><span class="line">                Node&lt;K, V&gt; next;</span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    next = e.next;</span><br><span class="line">                    <span class="comment">// 原位置节点</span></span><br><span class="line">                    <span class="keyword">if</span> ((e.hash &amp; oldCap) == <span class="number">0</span>) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (loTail == <span class="literal">null</span>) loHead = e;</span><br><span class="line">                        <span class="keyword">else</span> loTail.next = e;</span><br><span class="line">                        loTail = e;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="comment">// 扩容后的新位置节点</span></span><br><span class="line">                        <span class="keyword">if</span> (hiTail == <span class="literal">null</span>) hiHead = e;</span><br><span class="line">                        <span class="keyword">else</span> hiTail.next = e;</span><br><span class="line">                        hiTail = e;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">while</span> ((e = next) != <span class="literal">null</span>);</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 原位置放到新数组</span></span><br><span class="line">                <span class="keyword">if</span> (loTail != <span class="literal">null</span>) &#123;</span><br><span class="line">                    loTail.next = <span class="literal">null</span>;</span><br><span class="line">                    newTab[j] = loHead;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 新位置放到新数组</span></span><br><span class="line">                <span class="keyword">if</span> (hiTail != <span class="literal">null</span>) &#123;</span><br><span class="line">                    hiTail.next = <span class="literal">null</span>;</span><br><span class="line">                    newTab[j + oldCap] = hiHead;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> newTable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>扩容后元素位置规律</strong>：</p><ul><li><code>(e.hash &amp; oldCap) == 0</code> → 留在原位置</li><li><code>(e.hash &amp; oldCap) != 0</code> → 移动到原位置 + oldCap</li></ul><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    subgraph 扩容示意        A["oldCap=16"] --> B["oldCap=32"]        A --> C["元素位置不变 hash & 16 == 0"]        A --> D["位置 +16 hash & 16 != 0"]    end    style C fill:#c8e6c9    style D fill:#e3f2fd</pre></div><h3 id="ConcurrentHashMap-源码解析"><a href="#ConcurrentHashMap-源码解析" class="headerlink" title="ConcurrentHashMap 源码解析"></a>ConcurrentHashMap 源码解析</h3><h4 id="JDK-1-7-分段锁"><a href="#JDK-1-7-分段锁" class="headerlink" title="JDK 1.7 分段锁"></a>JDK 1.7 分段锁</h4><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["ConcurrentHashMap"] --> B["Segment[] 数组"]    B --> C["Segment 继承 ReentrantLock"]    C --> D["HashEntry[] table"]    D --> E["链表节点"]    style A fill:#fff3e0    style B fill:#e3f2fd</pre></div><p>每个 Segment 包含一个 HashEntry 数组，每个 HashEntry 是一个链表节点。并发度由 Segment 数组大小决定，默认 16。</p><h4 id="JDK-1-8-CAS-synchronized"><a href="#JDK-1-8-CAS-synchronized" class="headerlink" title="JDK 1.8 CAS + synchronized"></a>JDK 1.8 CAS + synchronized</h4><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["ConcurrentHashMap JDK 1.8"] --> B["Node[] table volatile 数组"]    A --> C["CAS 操作 保证原子性"]    A --> D["synchronized 锁桶头节点"]    C --> E["putVal"]    D --> E    E --> F{是否需要初始化}    F -->|是| G["initTable CAS 保证单线程"]    E --> H{key 是否存在}    H -->|是| I["覆盖 value"]    H -->|否| J{链表长度>8}    J -->|是| K["转为红黑树"]    J -->|否| L["尾插法"]    L --> M["检查扩容"]    K --> M    style A fill:#fff3e0</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMapDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        ConcurrentHashMap&lt;String, Integer&gt; map = </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// CAS + synchronized 保证了并发安全</span></span><br><span class="line">        map.put(<span class="string">&quot;A&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        map.putIfAbsent(<span class="string">&quot;B&quot;</span>, <span class="number">2</span>);  <span class="comment">// CAS 操作</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 原子操作</span></span><br><span class="line">        map.putIfAbsent(<span class="string">&quot;counter&quot;</span>, <span class="number">0</span>);</span><br><span class="line">        map.put(<span class="string">&quot;counter&quot;</span>, map.get(<span class="string">&quot;counter&quot;</span>) + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 推荐：使用 compute 原子更新</span></span><br><span class="line">        map.compute(<span class="string">&quot;counter&quot;</span>, (k, v) -&gt; v == <span class="literal">null</span> ? <span class="number">1</span> : v + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h2><h3 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分类"></a>设计模式分类</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["设计模式"] --> B["创建型模式"]    A --> C["结构型模式"]    A --> D["行为型模式"]    B --> B1["单例模式"]    B --> B2["工厂模式"]    B --> B3["建造者模式"]    B --> B4["原型模式"]    C --> C1["代理模式"]    C --> C2["装饰器模式"]    C --> C3["适配器模式"]    C --> C4["组合模式"]    D --> D1["策略模式"]    D --> D2["观察者模式"]    D --> D3["责任链模式"]    D --> D4["模板方法模式"]    style A fill:#fff3e0</pre></div><h3 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h3><p>确保一个类只有一个实例，并提供一个全局访问点。</p><h4 id="饿汉式（线程安全）"><a href="#饿汉式（线程安全）" class="headerlink" title="饿汉式（线程安全）"></a>饿汉式（线程安全）</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HungrySingleton</span> &#123;</span><br><span class="line">    <span class="comment">// 类加载时就创建实例，线程安全</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">HungrySingleton</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HungrySingleton</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">HungrySingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 防止反射创建多个实例</span></span><br><span class="line">        <span class="keyword">if</span> (INSTANCE != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;单例对象已存在！&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> HungrySingleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> INSTANCE;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="懒汉式（线程不安全）"><a href="#懒汉式（线程不安全）" class="headerlink" title="懒汉式（线程不安全）"></a>懒汉式（线程不安全）</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LazySingleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> LazySingleton instance;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">LazySingleton</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> LazySingleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (instance == <span class="literal">null</span>) &#123;  <span class="comment">// 线程不安全</span></span><br><span class="line">            instance = <span class="keyword">new</span> <span class="title class_">LazySingleton</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="双重检查锁定（线程安全，推荐）"><a href="#双重检查锁定（线程安全，推荐）" class="headerlink" title="双重检查锁定（线程安全，推荐）"></a>双重检查锁定（线程安全，推荐）</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DoubleCheckSingleton</span> &#123;</span><br><span class="line">    <span class="comment">// volatile 防止指令重排序</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> DoubleCheckSingleton instance;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">DoubleCheckSingleton</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> DoubleCheckSingleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (instance == <span class="literal">null</span>) &#123;  <span class="comment">// 第一次检查</span></span><br><span class="line">            <span class="keyword">synchronized</span> (DoubleCheckSingleton.class) &#123;</span><br><span class="line">                <span class="keyword">if</span> (instance == <span class="literal">null</span>) &#123;  <span class="comment">// 第二次检查</span></span><br><span class="line">                    instance = <span class="keyword">new</span> <span class="title class_">DoubleCheckSingleton</span>();</span><br><span class="line">                    <span class="comment">// 可能的指令重排序：</span></span><br><span class="line">                    <span class="comment">// 1. 分配内存</span></span><br><span class="line">                    <span class="comment">// 2. 调用构造函数</span></span><br><span class="line">                    <span class="comment">// 3. instance 指向内存</span></span><br><span class="line">                    <span class="comment">// volatile 防止重排序，保证 2 在 3 之前</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="静态内部类（线程安全，推荐）"><a href="#静态内部类（线程安全，推荐）" class="headerlink" title="静态内部类（线程安全，推荐）"></a>静态内部类（线程安全，推荐）</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StaticInnerClassSingleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">StaticInnerClassSingleton</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 静态内部类，只有在被调用时才会加载，且加载过程是线程安全的</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">StaticInnerClassSingleton</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">StaticInnerClassSingleton</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> StaticInnerClassSingleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h3><h4 id="简单工厂"><a href="#简单工厂" class="headerlink" title="简单工厂"></a>简单工厂</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 产品接口</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Phone</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体产品</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">IPhone</span> <span class="keyword">implements</span> <span class="title class_">Phone</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;用 iPhone 打电话&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AndroidPhone</span> <span class="keyword">implements</span> <span class="title class_">Phone</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;用安卓手机打电话&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SimplePhoneFactory</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Phone <span class="title function_">createPhone</span><span class="params">(String type)</span> &#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;iPhone&quot;</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">IPhone</span>();</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;Android&quot;</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AndroidPhone</span>();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;未知类型&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="工厂方法模式"><a href="#工厂方法模式" class="headerlink" title="工厂方法模式"></a>工厂方法模式</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 抽象工厂</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">PhoneFactory</span> &#123;</span><br><span class="line">    Phone <span class="title function_">createPhone</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">IPhoneFactory</span> <span class="keyword">implements</span> <span class="title class_">PhoneFactory</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Phone <span class="title function_">createPhone</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">IPhone</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AndroidFactory</span> <span class="keyword">implements</span> <span class="title class_">PhoneFactory</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Phone <span class="title function_">createPhone</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AndroidPhone</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FactoryMethodDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">PhoneFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IPhoneFactory</span>();</span><br><span class="line">        <span class="type">Phone</span> <span class="variable">phone</span> <span class="operator">=</span> factory.createPhone();</span><br><span class="line">        phone.call();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="抽象工厂模式"><a href="#抽象工厂模式" class="headerlink" title="抽象工厂模式"></a>抽象工厂模式</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 产品族接口</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Laptop</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">display</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Phone</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 苹果产品族</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MacBook</span> <span class="keyword">implements</span> <span class="title class_">Laptop</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">display</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MacBook 显示&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">iPhone</span> <span class="keyword">implements</span> <span class="title class_">Phone</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;iPhone 打电话&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 抽象工厂</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">DeviceFactory</span> &#123;</span><br><span class="line">    Laptop <span class="title function_">createLaptop</span><span class="params">()</span>;</span><br><span class="line">    Phone <span class="title function_">createPhone</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 苹果工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AppleFactory</span> <span class="keyword">implements</span> <span class="title class_">DeviceFactory</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Laptop <span class="title function_">createLaptop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MacBook</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Phone <span class="title function_">createPhone</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">iPhone</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="策略模式"><a href="#策略模式" class="headerlink" title="策略模式"></a>策略模式</h3><p>定义一系列算法，把它们一个个封装起来，并且使它们可以相互替换。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 策略接口</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">SortStrategy</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] array)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体策略：冒泡排序</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">BubbleSort</span> <span class="keyword">implements</span> <span class="title class_">SortStrategy</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;使用冒泡排序&quot;</span>);</span><br><span class="line">        <span class="comment">// 冒泡排序实现...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体策略：快速排序</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">QuickSort</span> <span class="keyword">implements</span> <span class="title class_">SortStrategy</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;使用快速排序&quot;</span>);</span><br><span class="line">        <span class="comment">// 快速排序实现...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 上下文：使用策略的对象</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Sorter</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> SortStrategy strategy;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setStrategy</span><span class="params">(SortStrategy strategy)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.strategy = strategy;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">        strategy.sort(array);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StrategyDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] array = &#123;<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">9</span>, <span class="number">2</span>, <span class="number">6</span>&#125;;</span><br><span class="line"></span><br><span class="line">        <span class="type">Sorter</span> <span class="variable">sorter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sorter</span>();</span><br><span class="line"></span><br><span class="line">        sorter.setStrategy(<span class="keyword">new</span> <span class="title class_">BubbleSort</span>());</span><br><span class="line">        sorter.sort(array);</span><br><span class="line"></span><br><span class="line">        sorter.setStrategy(<span class="keyword">new</span> <span class="title class_">QuickSort</span>());</span><br><span class="line">        sorter.sort(array);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h3><p>为其他对象提供一种代理以控制对这个对象的访问。</p><h4 id="静态代理"><a href="#静态代理" class="headerlink" title="静态代理"></a>静态代理</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 接口</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Image</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">display</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 真实对象</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RealImage</span> <span class="keyword">implements</span> <span class="title class_">Image</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String filename;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">RealImage</span><span class="params">(String filename)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.filename = filename;</span><br><span class="line">        loadFromDisk();  <span class="comment">// 模拟加载</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">display</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;显示图片：&quot;</span> + filename);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">loadFromDisk</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;从磁盘加载：&quot;</span> + filename);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 代理对象</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ImageProxy</span> <span class="keyword">implements</span> <span class="title class_">Image</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> RealImage realImage;</span><br><span class="line">    <span class="keyword">private</span> String filename;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ImageProxy</span><span class="params">(String filename)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.filename = filename;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">display</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 懒加载：真正需要时才加载真实对象</span></span><br><span class="line">        <span class="keyword">if</span> (realImage == <span class="literal">null</span>) &#123;</span><br><span class="line">            realImage = <span class="keyword">new</span> <span class="title class_">RealImage</span>(filename);</span><br><span class="line">        &#125;</span><br><span class="line">        realImage.display();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProxyDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Image</span> <span class="variable">image</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ImageProxy</span>(<span class="string">&quot;photo.jpg&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 此时不会加载真实图片</span></span><br><span class="line">        System.out.println(<span class="string">&quot;图片代理已创建&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 真正显示时才加载</span></span><br><span class="line">        image.display();</span><br><span class="line">        image.display();  <span class="comment">// 第二次不需要重新加载</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="JDK-动态代理"><a href="#JDK-动态代理" class="headerlink" title="JDK 动态代理"></a>JDK 动态代理</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JDKDynamicProxy</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">RealImage</span> <span class="variable">realImage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RealImage</span>(<span class="string">&quot;photo.jpg&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Image</span> <span class="variable">proxy</span> <span class="operator">=</span> (Image) Proxy.newProxyInstance(</span><br><span class="line">            RealImage.class.getClassLoader(),</span><br><span class="line">            RealImage.class.getInterfaces(),</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">InvocationHandler</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, </span></span><br><span class="line"><span class="params">                        Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;前置操作&quot;</span>);</span><br><span class="line">                    <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> method.invoke(realImage, args);</span><br><span class="line">                    System.out.println(<span class="string">&quot;后置操作&quot;</span>);</span><br><span class="line">                    <span class="keyword">return</span> result;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        proxy.display();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="常用工具类"><a href="#常用工具类" class="headerlink" title="常用工具类"></a>常用工具类</h2><h3 id="StringUtils-常用方法"><a href="#StringUtils-常用方法" class="headerlink" title="StringUtils 常用方法"></a>StringUtils 常用方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.commons.lang3.StringUtils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StringUtilsDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;  hello world  &quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断空</span></span><br><span class="line">        StringUtils.isEmpty(str);           <span class="comment">// false（有空格）</span></span><br><span class="line">        StringUtils.isBlank(str);            <span class="comment">// false（有空格）</span></span><br><span class="line">        StringUtils.isBlank(<span class="string">&quot;&quot;</span>);             <span class="comment">// true</span></span><br><span class="line">        StringUtils.isBlank(<span class="literal">null</span>);           <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 去除空白</span></span><br><span class="line">        StringUtils.trim(str);              <span class="comment">// &quot;hello world&quot;</span></span><br><span class="line">        StringUtils.trimToNull(str);         <span class="comment">// &quot;hello world&quot;，空白返回null</span></span><br><span class="line">        StringUtils.trimToEmpty(str);        <span class="comment">// &quot;hello world&quot;，空白返回&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断相等</span></span><br><span class="line">        StringUtils.equals(<span class="string">&quot;abc&quot;</span>, <span class="string">&quot;abc&quot;</span>);    <span class="comment">// true</span></span><br><span class="line">        StringUtils.equalsIgnoreCase(<span class="string">&quot;abc&quot;</span>, <span class="string">&quot;ABC&quot;</span>);  <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 字符串包含</span></span><br><span class="line">        StringUtils.contains(<span class="string">&quot;abc&quot;</span>, <span class="string">&quot;b&quot;</span>);   <span class="comment">// true</span></span><br><span class="line">        StringUtils.containsIgnoreCase(<span class="string">&quot;ABC&quot;</span>, <span class="string">&quot;b&quot;</span>);  <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 截取</span></span><br><span class="line">        StringUtils.substring(<span class="string">&quot;hello world&quot;</span>, <span class="number">6</span>);     <span class="comment">// &quot;world&quot;</span></span><br><span class="line">        StringUtils.substring(<span class="string">&quot;hello world&quot;</span>, <span class="number">0</span>, <span class="number">5</span>);  <span class="comment">// &quot;hello&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 分割</span></span><br><span class="line">        StringUtils.split(<span class="string">&quot;a,b,c&quot;</span>, <span class="string">&quot;,&quot;</span>);     <span class="comment">// [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 连接</span></span><br><span class="line">        StringUtils.join([<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;c&quot;</span>], <span class="string">&quot;-&quot;</span>);  <span class="comment">// &quot;a-b-c&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 重复</span></span><br><span class="line">        StringUtils.repeat(<span class="string">&quot;ab&quot;</span>, <span class="number">3</span>);         <span class="comment">// &quot;ababab&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 反转</span></span><br><span class="line">        StringUtils.reverse(<span class="string">&quot;hello&quot;</span>);        <span class="comment">// &quot;olleh&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 大小写</span></span><br><span class="line">        StringUtils.upperCase(<span class="string">&quot;hello&quot;</span>);      <span class="comment">// &quot;HELLO&quot;</span></span><br><span class="line">        StringUtils.lowerCase(<span class="string">&quot;HELLO&quot;</span>);      <span class="comment">// &quot;hello&quot;</span></span><br><span class="line">        StringUtils.capitalize(<span class="string">&quot;hello&quot;</span>);    <span class="comment">// &quot;Hello&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="CollectionUtils-常用方法"><a href="#CollectionUtils-常用方法" class="headerlink" title="CollectionUtils 常用方法"></a>CollectionUtils 常用方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.commons.collections4.CollectionUtils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CollectionUtilsDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        List&lt;String&gt; list1 = Arrays.asList(<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>);</span><br><span class="line">        List&lt;String&gt; list2 = Arrays.asList(<span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>, <span class="string">&quot;D&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断空</span></span><br><span class="line">        CollectionUtils.isEmpty(list1);     <span class="comment">// false</span></span><br><span class="line">        CollectionUtils.isNotEmpty(list1);  <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 交集</span></span><br><span class="line">        List&lt;String&gt; intersection = </span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(CollectionUtils.intersection(list1, list2));</span><br><span class="line">        <span class="comment">// [B, C]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 并集</span></span><br><span class="line">        CollectionUtils.union(list1, list2);</span><br><span class="line">        <span class="comment">// [A, B, C, D]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 差集（list1 - list2）</span></span><br><span class="line">        CollectionUtils.subtract(list1, list2);</span><br><span class="line">        <span class="comment">// [A]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断是否有交集</span></span><br><span class="line">        CollectionUtils.containsAny(list1, list2);  <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 过滤</span></span><br><span class="line">        List&lt;String&gt; filtered = list1.stream()</span><br><span class="line">            .filter(s -&gt; s.equals(<span class="string">&quot;A&quot;</span>))</span><br><span class="line">            .collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判空并赋值默认值</span></span><br><span class="line">        List&lt;String&gt; safeList = </span><br><span class="line">            CollectionUtils.isEmpty(list1) ? Collections.emptyList() : list1;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Objects-常用方法"><a href="#Objects-常用方法" class="headerlink" title="Objects 常用方法"></a>Objects 常用方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Objects;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ObjectsDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 判断相等</span></span><br><span class="line">        Objects.equals(<span class="string">&quot;abc&quot;</span>, <span class="string">&quot;abc&quot;</span>);       <span class="comment">// true</span></span><br><span class="line">        Objects.equals(<span class="literal">null</span>, <span class="string">&quot;abc&quot;</span>);        <span class="comment">// false</span></span><br><span class="line">        Objects.equals(<span class="literal">null</span>, <span class="literal">null</span>);         <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断是否为 null</span></span><br><span class="line">        Objects.isNull(<span class="literal">null</span>);               <span class="comment">// true</span></span><br><span class="line">        Objects.nonNull(<span class="literal">null</span>);              <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// requireNonNull：参数校验</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method</span><span class="params">(String param)</span> &#123;</span><br><span class="line">            <span class="comment">// param 为 null 时抛出 NullPointerException</span></span><br><span class="line">            Objects.requireNonNull(param, <span class="string">&quot;参数不能为空&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// toString 安全版本</span></span><br><span class="line">        Objects.toString(<span class="literal">null</span>);             <span class="comment">// &quot;null&quot;</span></span><br><span class="line">        Objects.toString(<span class="literal">null</span>, <span class="string">&quot;默认值&quot;</span>);   <span class="comment">// &quot;默认值&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// hashCode（对 null 返回 0）</span></span><br><span class="line">        Objects.hashCode(<span class="literal">null</span>);             <span class="comment">// 0</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Optional-最佳实践"><a href="#Optional-最佳实践" class="headerlink" title="Optional 最佳实践"></a>Optional 最佳实践</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Optional;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OptionalDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建 Optional</span></span><br><span class="line">        Optional&lt;String&gt; empty = Optional.empty();</span><br><span class="line">        Optional&lt;String&gt; of = Optional.of(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line">        Optional&lt;String&gt; nullable = Optional.ofNullable(<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断并获取</span></span><br><span class="line">        of.isPresent();                      <span class="comment">// true</span></span><br><span class="line">        of.isEmpty();                        <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取值（可能抛异常）</span></span><br><span class="line">        of.get();                           <span class="comment">// &quot;hello&quot;</span></span><br><span class="line">        <span class="comment">// nullable.get();                  // NoSuchElementException</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取值或默认值</span></span><br><span class="line">        nullable.orElse(<span class="string">&quot;default&quot;</span>);         <span class="comment">// &quot;default&quot;</span></span><br><span class="line">        nullable.orElseGet(() -&gt; <span class="string">&quot;computed&quot;</span>);  <span class="comment">// &quot;computed&quot;</span></span><br><span class="line">        <span class="comment">// nullable.orElseThrow();          // NoSuchElementException</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ifPresent</span></span><br><span class="line">        of.ifPresent(System.out::println);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// map 转换</span></span><br><span class="line">        of.map(String::toUpperCase);         <span class="comment">// Optional[&quot;HELLO&quot;]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// flatMap（返回 Optional）</span></span><br><span class="line">        of.flatMap(s -&gt; Optional.of(s.trim()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// filter 过滤</span></span><br><span class="line">        of.filter(s -&gt; s.length() &gt; <span class="number">3</span>);     <span class="comment">// Optional[&quot;hello&quot;]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 链式调用</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> Optional.ofNullable(user)</span><br><span class="line">            .map(User::getName)</span><br><span class="line">            .map(String::toUpperCase)</span><br><span class="line">            .orElse(<span class="string">&quot;匿名&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="📌-总结"><a href="#📌-总结" class="headerlink" title="📌 总结"></a>📌 总结</h2><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Java 进阶))    JVM      内存模型        程序计数器        虚拟机栈        本地方法栈        堆        方法区      垃圾回收        标记-清除        复制算法        标记-整理        分代收集      类加载        双亲委派        自定义加载器    并发编程      三大概念        原子性        可见性        有序性      volatile      synchronized      线程池      JUC 工具类    集合源码      HashMap      ConcurrentHashMap    设计模式      单例      工厂      策略      代理</pre></div><h3 id="进阶学习建议"><a href="#进阶学习建议" class="headerlink" title="进阶学习建议"></a>进阶学习建议</h3><ol><li><strong>多读源码</strong>：JDK 源码、Spring 源码、MyBatis 源码</li><li><strong>手写实现</strong>：自己实现一遍 HashMap、线程池等</li><li><strong>性能调优</strong>：学习 JVM 调优、SQL 调优</li><li><strong>分布式</strong>：学习 Redis、RocketMQ、Kafka</li><li><strong>微服务</strong>：Spring Cloud、Dubbo、Service Mesh</li></ol><blockquote><p>向下扎根，向上生长 🌱</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 20 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/java-advanced/</id>
    <link href="https://blog.codenav.top/java-advanced/"/>
    <published>2026-05-20T02:00:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Java-进阶核心知识点总结-🚀"><a href="#Java-进阶核心知识点总结-🚀" class="headerlink" title="Java 进阶核心知识点总结 🚀"></a>Java 进阶核心知识点总结 🚀</h1><blockquote>]]>
    </summary>
    <title>Java 进阶核心知识点总结</title>
    <updated>2026-05-24T05:21:35.489Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <content>
      <![CDATA[<h1 id="你好，欢迎来到旅人小站-👋"><a href="#你好，欢迎来到旅人小站-👋" class="headerlink" title="你好，欢迎来到旅人小站 👋"></a>你好，欢迎来到旅人小站 👋</h1><p>你好！我是<strong>旅人</strong>，一名热爱技术的程序员。</p><p>这个博客从零开始搭建，记录我在编程道路上的学习心得、踩坑经历和成长轨迹。如果你恰好路过，希望能给你带来一点点帮助。</p><hr><h2 id="🏠-关于这个博客"><a href="#🏠-关于这个博客" class="headerlink" title="🏠 关于这个博客"></a>🏠 关于这个博客</h2><p>这是一个用 <strong>Hexo + Butterfly</strong> 主题搭建的个人技术博客，托管在 GitHub Pages 上。选择这个方案是因为：</p><ul><li>✅ 纯静态站点，访问速度快</li><li>✅ Markdown 写作，专注内容</li><li>✅ 主题美观，配置灵活</li><li>✅ 免费托管，省心省力</li></ul><h3 id="为什么建站？"><a href="#为什么建站？" class="headerlink" title="为什么建站？"></a>为什么建站？</h3><ul><li><strong>沉淀知识</strong>：看过的东西容易忘，写下来才能真正变成自己的</li><li><strong>锻炼写作</strong>：技术文档能力很重要，博客是个好练习场</li><li><strong>分享交流</strong>：如果碰巧能帮到别人，那更是意外惊喜</li><li><strong>个人品牌</strong>：慢慢积累，建立自己的技术影响力</li></ul><h3 id="建站目的"><a href="#建站目的" class="headerlink" title="建站目的"></a>建站目的</h3><ol><li>📝 记录学习过程，方便日后回顾</li><li>📚 整理知识体系，把零散的内容系统化</li><li>🤝 分享实用技术，帮助有需要的人</li><li>🌱 结交志同道合的朋友，一起进步</li></ol><hr><h2 id="🛠️-我的技术栈"><a href="#🛠️-我的技术栈" class="headerlink" title="🛠️ 我的技术栈"></a>🛠️ 我的技术栈</h2><h3 id="Java-后端"><a href="#Java-后端" class="headerlink" title="Java 后端"></a>Java 后端</h3><ul><li>Java 核心基础：集合、多线程、I&#x2F;O、反射、泛型</li><li>JVM 内存模型、类加载机制、性能调优</li><li>Spring Boot、Spring MVC、Spring Cloud 微服务开发</li><li>Spring Security 权限控制</li><li>MyBatis &#x2F; MyBatis-Plus 数据持久层</li></ul><h3 id="前端技术"><a href="#前端技术" class="headerlink" title="前端技术"></a>前端技术</h3><ul><li>HTML、CSS、JavaScript 基础</li><li>Vue、React 框架入门</li><li>Element-Plus、Vant、Ant Design 等组件库</li><li>可进行简单项目开发</li></ul><h3 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h3><table><thead><tr><th>类型</th><th>技能</th></tr></thead><tbody><tr><td>MySQL</td><td>索引优化、事务隔离级别、SQL 调优</td></tr><tr><td>Redis</td><td>缓存、分布式锁、会话管理</td></tr><tr><td>MongoDB</td><td>非关系型数据库基本使用</td></tr></tbody></table><h3 id="中间件与-DevOps"><a href="#中间件与-DevOps" class="headerlink" title="中间件与 DevOps"></a>中间件与 DevOps</h3><ul><li><strong>消息队列</strong>：RabbitMQ &#x2F; Kafka</li><li><strong>服务注册</strong>：Nacos &#x2F; Eureka，OpenFeign 服务调用</li><li><strong>分布式事务</strong>：了解 Seata 基本概念</li><li><strong>容器化</strong>：Docker</li><li><strong>版本控制</strong>：Git</li><li><strong>项目构建</strong>：Maven</li><li><strong>AI 辅助</strong>：Cursor、Claude AI 等工具</li></ul><blockquote><p>详情可以看看 <a href="/about/">关于我</a> 页面 👈</p></blockquote><hr><h2 id="📌-内容规划"><a href="#📌-内容规划" class="headerlink" title="📌 内容规划"></a>📌 内容规划</h2><p>接下来的文章会覆盖但不限于：</p><h3 id="后端开发"><a href="#后端开发" class="headerlink" title="后端开发"></a>后端开发</h3><ul><li>Java 核心技术深度解析</li><li>Spring 全家桶实战经验</li><li>微服务架构设计思路</li><li>性能调优与问题排查</li></ul><h3 id="数据库-1"><a href="#数据库-1" class="headerlink" title="数据库"></a>数据库</h3><ul><li>MySQL 进阶：索引、事务、优化</li><li>Redis 深入：缓存策略、分布式场景</li><li>NoSQL 实践：MongoDB 的使用</li></ul><h3 id="前端笔记"><a href="#前端笔记" class="headerlink" title="前端笔记"></a>前端笔记</h3><ul><li>前端基础巩固</li><li>Vue3 响应式原理探索</li><li>组件库使用技巧</li></ul><h3 id="工具效率"><a href="#工具效率" class="headerlink" title="工具效率"></a>工具效率</h3><ul><li>Git 高级操作</li><li>Docker 容器化部署</li><li>AI 编程工具使用心得</li></ul><h3 id="踩坑记录"><a href="#踩坑记录" class="headerlink" title="踩坑记录"></a>踩坑记录</h3><ul><li>开发过程中遇到的问题与解决方案</li><li>少走弯路的经验分享</li></ul><hr><h2 id="💭-我的学习态度"><a href="#💭-我的学习态度" class="headerlink" title="💭 我的学习态度"></a>💭 我的学习态度</h2><p>学习这件事，<strong>没有捷径，只有积累</strong>。</p><p>看视频觉得懂了，听讲解觉得会了，但真正掌握还是在动手实践的时候。所以这个博客更像是我的<strong>笔记本</strong>，记录真实遇到的问题和解决方法，而不是复制粘贴的笔记。</p><blockquote><p>眼过千遍不如手过一遍 🚀</p></blockquote><hr><h2 id="🎯-写作原则"><a href="#🎯-写作原则" class="headerlink" title="🎯 写作原则"></a>🎯 写作原则</h2><ul><li>✍️ 内容为王，写有价值的文章</li><li>🔍 拒绝水文，每篇都有干货</li><li>🐛 真实踩坑，不纸上谈兵</li><li>📈 持续更新，保持内容新鲜</li></ul><hr><h2 id="💬-写在最后"><a href="#💬-写在最后" class="headerlink" title="💬 写在最后"></a>💬 写在最后</h2><blockquote><p>“Talk is cheap, show me the code.”</p><p>— Linus Torvalds</p></blockquote><p>代码是最好的语言。希望这个博客能见证我的成长，也希望能和你一起进步。</p><p>如果有疑问或建议，欢迎留言交流！期待与各位技术同好切磋~</p><hr><p><em>📅 博客建于 2026 年 5 月</em></p>]]>
    </content>
    <id>https://blog.codenav.top/hello-world/</id>
    <link href="https://blog.codenav.top/hello-world/"/>
    <published>2026-05-20T01:00:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="你好，欢迎来到旅人小站-👋"><a href="#你好，欢迎来到旅人小站-👋" class="headerlink" title="你好，欢迎来到旅人小站 👋"></a>你好，欢迎来到旅人小站 👋</h1><p>你好！我是<strong>旅人</stron]]>
    </summary>
    <title>你好，欢迎来到旅人小站</title>
    <updated>2026-05-20T00:25:28.977Z</updated>
  </entry>
  <entry>
    <author>
      <name>一个旅人</name>
    </author>
    <category term="Java" scheme="https://blog.codenav.top/categories/Java/"/>
    <category term="Java" scheme="https://blog.codenav.top/tags/Java/"/>
    <category term="基础知识" scheme="https://blog.codenav.top/tags/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    <content>
      <![CDATA[<h1 id="Java-基础核心总结-📚"><a href="#Java-基础核心总结-📚" class="headerlink" title="Java 基础核心总结 📚"></a>Java 基础核心总结 📚</h1><blockquote><p>Java 是一门面向对象的编程语言，由 Sun 公司于 1995 年发布。它具有<strong>跨平台</strong>（一次编写，到处运行）、<strong>安全性高</strong>（没有指针，垃圾自动回收）、<strong>生态丰富</strong>（开源框架众多）等特点。本文将系统总结 Java 基础知识体系，帮助大家巩固核心概念，建立完整的知识框架。</p></blockquote><hr><h2 id="📖-目录"><a href="#📖-目录" class="headerlink" title="📖 目录"></a>📖 目录</h2><ol><li><a href="#java-%E7%A8%8B%E5%BA%8F%E8%BF%90%E8%A1%8C%E6%B5%81%E7%A8%8B">Java 程序运行流程</a></li><li><a href="#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B">数据类型</a></li><li><a href="#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%9F%BA%E7%A1%80">面向对象基础</a></li><li><a href="#%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6">集合框架</a></li><li><a href="#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80">多线程基础</a></li><li><a href="#io-%E6%B5%81">I&#x2F;O 流</a></li><li><a href="#%E6%B3%9B%E5%9E%8B">泛型</a></li><li><a href="#%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6">反射机制</a></li></ol><hr><h2 id="Java-程序运行流程"><a href="#Java-程序运行流程" class="headerlink" title="Java 程序运行流程"></a>Java 程序运行流程</h2><h3 id="什么是字节码？"><a href="#什么是字节码？" class="headerlink" title="什么是字节码？"></a>什么是字节码？</h3><p>Java 代码最终不会直接编译成机器码，而是编译成一种中间形式的字节码（.class 文件）。这种字节码不能被任何操作系统直接执行，但可以被 Java 虚拟机（JVM）解释执行或即时编译（JIT）成机器码。</p><p>这样做的好处是：<strong>同一个 .class 文件可以在任何安装了 JVM 的操作系统上运行</strong>，这正是 Java 跨平台的核心原理。</p><h3 id="整体执行流程"><a href="#整体执行流程" class="headerlink" title="整体执行流程"></a>整体执行流程</h3><p>Java 程序的执行流程如下：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A[".java 源文件"] --> B["JavaC 编译"]    B --> C[".class 字节码文件"]    C --> D["类加载器 ClassLoader"]    D --> E["字节码验证器"]    E --> F["JVM 执行引擎"]    F --> G["操作系统平台"]    G --> H["硬件"]    style A fill:#e1f5ff    style C fill:#fff3e0    style H fill:#e8f5e9</pre></div><h3 id="各阶段详细说明"><a href="#各阶段详细说明" class="headerlink" title="各阶段详细说明"></a>各阶段详细说明</h3><table><thead><tr><th>阶段</th><th>组件</th><th>具体做什么</th></tr></thead><tbody><tr><td><strong>编写</strong></td><td>开发者</td><td>编写 .java 源文件</td></tr><tr><td><strong>编译</strong></td><td>JavaC</td><td>将 .java 翻译成 .class 字节码</td></tr><tr><td><strong>加载</strong></td><td>ClassLoader</td><td>把 .class 文件加载到内存中</td></tr><tr><td><strong>验证</strong></td><td>字节码验证器</td><td>检查字节码是否符合 JVM 规范</td></tr><tr><td><strong>执行</strong></td><td>JVM 执行引擎</td><td>解释执行或 JIT 编译执行</td></tr><tr><td><strong>运行</strong></td><td>OS + Hardware</td><td>最终在具体硬件上运行</td></tr></tbody></table><h3 id="JVM-内存划分简介"><a href="#JVM-内存划分简介" class="headerlink" title="JVM 内存划分简介"></a>JVM 内存划分简介</h3><p>JVM 在执行程序时，会把内存划分成几个区域来管理不同类型的数据：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["JVM 内存划分"] --> B["堆 Heap"]    A --> C["栈 Stack"]    A --> D["方法区 Method Area"]    A --> E["本地方法栈 Native Stack"]    A --> F["程序计数器 PC"]    B --> B1["对象实例"]    B --> B2["数组"]    C --> C1["方法调用"]    C --> C2["局部变量"]    C --> C3["操作数栈"]    D --> D1["类信息"]    D --> D2["常量池"]    D --> D3["静态变量"]    style B fill:#e8f5e9    style C fill:#e3f2fd    style D fill:#fff3e0</pre></div><h3 id="第一个-Java-程序"><a href="#第一个-Java-程序" class="headerlink" title="第一个 Java 程序"></a>第一个 Java 程序</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorld</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Hello, Java!&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>代码解析：</strong></p><ul><li><code>public class HelloWorld</code> — 定义一个公开的类，类名必须与文件名相同</li><li><code>public static void main(String[] args)</code> — 程序入口方法，JVM 从这里开始执行</li><li><code>System.out.println(...)</code> — 向控制台输出内容并换行</li></ul><p>将上述代码保存为 <code>HelloWorld.java</code>，在命令行执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">javac HelloWorld.java   <span class="comment"># 编译</span></span><br><span class="line">java HelloWorld         <span class="comment"># 运行</span></span><br></pre></td></tr></table></figure><hr><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><h3 id="为什么需要数据类型？"><a href="#为什么需要数据类型？" class="headerlink" title="为什么需要数据类型？"></a>为什么需要数据类型？</h3><p>计算机内存中存储的是二进制数据，但不同类型的数据占用的空间不同、表示的意义也不同。<strong>数据类型就是对数据的一种分类，告诉 JVM 应该以什么方式存储和处理这些数据。</strong></p><p>Java 中的数据类型分为两大类：<strong>基本数据类型</strong>和<strong>引用数据类型</strong>。基本数据类型存储的是具体的值，而引用数据类型存储的是对象在内存中的地址（引用）。</p><h3 id="两大类型分类"><a href="#两大类型分类" class="headerlink" title="两大类型分类"></a>两大类型分类</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    subgraph 数据类型        A["数据类型"] --> B["基本数据类型"]        A --> C["引用数据类型"]        B --> B1["整型"]        B --> B2["浮点型"]        B --> B3["字符型"]        B --> B4["布尔型"]        C --> C1["类"]        C --> C2["接口"]        C --> C3["数组"]    end    style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#e8f5e9</pre></div><h3 id="基本数据类型（8种）"><a href="#基本数据类型（8种）" class="headerlink" title="基本数据类型（8种）"></a>基本数据类型（8种）</h3><p>Java 定义了 8 种基本数据类型，它们是 Java 语言的基础组成部分。</p><table><thead><tr><th>类型</th><th>关键字</th><th>占用空间</th><th>取值范围</th><th>默认值</th><th>示例</th></tr></thead><tbody><tr><td>字节型</td><td>byte</td><td>1 字节</td><td>-128 ~ 127</td><td>0</td><td><code>byte b = 100;</code></td></tr><tr><td>短整型</td><td>short</td><td>2 字节</td><td>-32768 ~ 32767</td><td>0</td><td><code>short s = 1000;</code></td></tr><tr><td>整型</td><td>int</td><td>4 字节</td><td>-2³¹ ~ 2³¹-1（约21亿）</td><td>0</td><td><code>int i = 100000;</code></td></tr><tr><td>长整型</td><td>long</td><td>8 字节</td><td>-2⁶³ ~ 2⁶³-1</td><td>0L</td><td><code>long l = 1000000L;</code></td></tr><tr><td>单精度浮点</td><td>float</td><td>4 字节</td><td>IEEE 754 标准</td><td>0.0f</td><td><code>float f = 3.14f;</code></td></tr><tr><td>双精度浮点</td><td>double</td><td>8 字节</td><td>IEEE 754 标准</td><td>0.0d</td><td><code>double d = 3.14159;</code></td></tr><tr><td>字符型</td><td>char</td><td>2 字节</td><td>Unicode 0 ~ 65535</td><td>‘\u0000’</td><td><code>char c = &#39;A&#39;;</code></td></tr><tr><td>布尔型</td><td>boolean</td><td>1&#x2F;4 字节</td><td>true &#x2F; false</td><td>false</td><td><code>boolean flag = true;</code></td></tr></tbody></table><h3 id="使用建议"><a href="#使用建议" class="headerlink" title="使用建议"></a>使用建议</h3><ul><li>**整数类型优先使用 <code>int</code>**，如果数值可能超过 21 亿才用 <code>long</code></li><li>**浮点类型优先使用 <code>double</code>**，因为 <code>float</code> 精度较低</li><li><strong><code>long</code> 类型数字字面量必须加 <code>L</code> 后缀</strong>，否则会当作 int 处理</li><li><strong><code>float</code> 类型数字字面量必须加 <code>f</code> 后缀</strong>，否则会当作 double 处理</li></ul><h3 id="类型转换详解"><a href="#类型转换详解" class="headerlink" title="类型转换详解"></a>类型转换详解</h3><p><strong>自动类型转换（隐式转换）</strong>：容量小的类型可以自动转换为容量大的类型。</p><p>转换顺序如下：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["byte"] --> B["short"]    B --> C["int"]    C --> D["long"]    D --> E["float"]    E --> F["double"]    A --> G["char"]    G --> C    style A fill:#e3f2fd    style F fill:#e8f5e9</pre></div><p><strong>强制类型转换（显式转换）</strong>：容量大的类型需要强制转换，可能丢失精度。</p><h3 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a>代码示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DataTypes</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// ============ 整型 ============</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">age</span> <span class="operator">=</span> <span class="number">25</span>;                    <span class="comment">// 普通整数，默认 int 类型</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">population</span> <span class="operator">=</span> <span class="number">7800000000L</span>;   <span class="comment">// 超大整数必须加 L</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 浮点型 ============</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">pi</span> <span class="operator">=</span> <span class="number">3.1415926</span>;           <span class="comment">// double 是 Java 浮点型的默认类型</span></span><br><span class="line">        <span class="type">float</span> <span class="variable">gravity</span> <span class="operator">=</span> <span class="number">9.8f</span>;            <span class="comment">// float 必须加 f 后缀</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 字符型 ============</span></span><br><span class="line">        <span class="type">char</span> <span class="variable">grade</span> <span class="operator">=</span> <span class="string">&#x27;A&#x27;</span>;                <span class="comment">// 单引号包裹单个字符</span></span><br><span class="line">        <span class="type">char</span> <span class="variable">chinese</span> <span class="operator">=</span> <span class="string">&#x27;\u4e2d&#x27;</span>;        <span class="comment">// Unicode 编码表示中文&quot;中&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 布尔型 ============</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isStudent</span> <span class="operator">=</span> <span class="literal">true</span>;        <span class="comment">// true 或 false，不能用 0/1 代替</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">hasJob</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 自动类型转换 ============</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">d</span> <span class="operator">=</span> age;                  <span class="comment">// int 自动转换为 double</span></span><br><span class="line">        <span class="comment">// 转换过程：int(25) -&gt; double(25.0)</span></span><br><span class="line">        System.out.println(<span class="string">&quot;自动转换：int 25 转 double = &quot;</span> + d);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 强制类型转换 ============</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> (<span class="type">int</span>) <span class="number">3.14</span>;              <span class="comment">// double 强制转为 int，小数部分丢失</span></span><br><span class="line">        System.out.println(<span class="string">&quot;强制转换：double 3.14 转 int = &quot;</span> + i);  <span class="comment">// 输出 3</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 强制转换可能丢失精度的情况</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">bigNum</span> <span class="operator">=</span> <span class="number">1000000L</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">smallNum</span> <span class="operator">=</span> (<span class="type">int</span>) bigNum;     <span class="comment">// 安全，因为 long 在 int 范围内</span></span><br><span class="line">        System.out.println(<span class="string">&quot;安全转换：&quot;</span> + smallNum);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 常见错误 ============</span></span><br><span class="line">        <span class="comment">// float f = 3.14;               // 错误！3.14 默认是 double</span></span><br><span class="line">        <span class="type">float</span> <span class="variable">f</span> <span class="operator">=</span> <span class="number">3.14f</span>;                 <span class="comment">// 正确写法</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// long l = 1000000;             // 1000000 默认是 int，可能溢出</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">1000000L</span>;               <span class="comment">// 正确写法</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="面向对象基础"><a href="#面向对象基础" class="headerlink" title="面向对象基础"></a>面向对象基础</h2><h3 id="什么是面向对象？"><a href="#什么是面向对象？" class="headerlink" title="什么是面向对象？"></a>什么是面向对象？</h3><p><strong>面向对象（OOP - Object Oriented Programming）</strong> 是一种编程思想，它把现实世界中的事物抽象成对象，用对象之间的关系来描述问题。</p><p>与之对应的是<strong>面向过程</strong>编程，比如 C 语言，它更关注”一步一步怎么做”，而面向对象更关注”谁来做这件事”。</p><h3 id="为什么要面向对象？"><a href="#为什么要面向对象？" class="headerlink" title="为什么要面向对象？"></a>为什么要面向对象？</h3><p>想象一个场景：我们需要描述一个学生管理系统。</p><p><strong>面向过程</strong>思考方式：</p><ol><li>定义学生的学号、姓名、成绩等变量</li><li>编写函数处理学生数据：添加学生、删除学生、查询成绩</li><li>用数组或链表存储所有学生</li></ol><p><strong>面向对象</strong>思考方式：</p><ol><li>定义一个 <code>Student</code> 类，封装学号、姓名、成绩</li><li>定义一个 <code>StudentManager</code> 类，管理所有学生对象</li><li>操作变成了：<code>manager.addStudent(s1)</code>、<code>s1.getScore()</code></li></ol><p>面向对象的优势在于：<strong>代码更易维护、复用性更高、更符合人类思维习惯</strong>。</p><h3 id="面向对象三大特性"><a href="#面向对象三大特性" class="headerlink" title="面向对象三大特性"></a>面向对象三大特性</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["面向对象特性"] --> B["封装 Encapsulation"]    A --> C["继承 Inheritance"]    A --> D["多态 Polymorphism"]    B --> B1["访问修饰符"]    B --> B2["getter/setter"]    B --> B3["数据保护"]    C --> C1["extends 关键字"]    C --> C2["方法重写 @Override"]    C --> C3["子类父类关系"]    D --> D1["重载 Overload"]    D --> D2["接口实现"]    D --> D3["父类引用指向子类对象"]    style A fill:#fff3e0    style B fill:#e3f2fd    style C fill:#e8f5e9    style D fill:#fce4ec</pre></div><h3 id="1-封装"><a href="#1-封装" class="headerlink" title="1. 封装"></a>1. 封装</h3><p><strong>封装</strong>是把属性和方法包装在一起，对外提供接口，隐藏内部实现细节。这就像一台电视，我们只需要知道怎么用遥控器，不需要知道内部的电路原理。</p><p>封装的两个核心要点：</p><ul><li><strong>属性私有化</strong>：用 <code>private</code> 修饰属性，不让外部直接访问</li><li><strong>提供公共方法</strong>：通过 <code>public</code> 的 getter&#x2F;setter 允许外部访问和修改</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="comment">// ============ 私有属性 ============</span></span><br><span class="line">    <span class="comment">// private 修饰符表示只能在 Person 类内部访问</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 构造方法 ============</span></span><br><span class="line">    <span class="comment">// 无参构造</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 有参构造</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name, <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="comment">// 通过 setAge 方法赋值，可以利用其中的校验逻辑</span></span><br><span class="line">        setAge(age);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ Getter &amp; Setter ============</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// setAge 方法中可以加入数据校验</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (age &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;年龄不能为负数！&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (age &gt; <span class="number">150</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;年龄不合理！&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>使用示例：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EncapsulationTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">&quot;张三&quot;</span>, <span class="number">25</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;姓名：&quot;</span> + person.getName());</span><br><span class="line">        System.out.println(<span class="string">&quot;年龄：&quot;</span> + person.getAge());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 尝试设置非法年龄</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            person.setAge(-<span class="number">5</span>);  <span class="comment">// 会抛出异常</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalArgumentException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;捕获异常：&quot;</span> + e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;最终年龄：&quot;</span> + person.getAge());  <span class="comment">// 仍是 25</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-继承"><a href="#2-继承" class="headerlink" title="2. 继承"></a>2. 继承</h3><p><strong>继承</strong>是面向对象的核心特性之一，它允许我们创建一个新类（子类）来继承另一个类（父类），子类可以复用父类的属性和方法，还可以扩展自己的功能。</p><p>为什么要继承？</p><ul><li><strong>代码复用</strong>：子类可以直接使用父类的代码</li><li><strong>建立类层次</strong>：体现类之间的”is-a”关系（比如”狗 is a 动物”）</li><li><strong>多态基础</strong>：没有继承就没有多态</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ============ 父类：动物 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="comment">// protected 修饰符：允许子类访问，但对外隐藏</span></span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Animal</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 动物都会吃，但具体吃什么由子类决定</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在吃东西...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 动物都会睡觉</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sleep</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在睡觉...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 子类：狗 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="comment">// 狗类独有的属性</span></span><br><span class="line">    <span class="keyword">private</span> String breed;  <span class="comment">// 品种</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Dog</span><span class="params">(String name, String breed)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(name);        <span class="comment">// super() 调用父类构造，必须放在第一行</span></span><br><span class="line">        <span class="built_in">this</span>.breed = breed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重写父类的 eat 方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot;（品种：&quot;</span> + breed + <span class="string">&quot;）正在吃狗粮...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 狗类独有的方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">bark</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 汪汪叫！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Getter</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getBreed</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> breed;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 子类：猫 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> indoorCat;  <span class="comment">// 是否是家猫</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Cat</span><span class="params">(String name, <span class="type">boolean</span> indoorCat)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(name);</span><br><span class="line">        <span class="built_in">this</span>.indoorCat = indoorCat;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 正在吃猫粮...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 猫独有的方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">meow</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(name + <span class="string">&quot; 喵呜~&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>测试代码：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InheritanceTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Dog</span> <span class="variable">dog</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>(<span class="string">&quot;旺财&quot;</span>, <span class="string">&quot;金毛&quot;</span>);</span><br><span class="line">        <span class="type">Cat</span> <span class="variable">cat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">&quot;咪咪&quot;</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用从父类继承的方法</span></span><br><span class="line">        dog.sleep();</span><br><span class="line">        cat.sleep();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用子类重写的方法</span></span><br><span class="line">        dog.eat();   <span class="comment">// 输出：旺财（品种：金毛）正在吃狗粮...</span></span><br><span class="line">        cat.eat();   <span class="comment">// 输出：咪咪正在吃猫粮...</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用子类独有的方法</span></span><br><span class="line">        dog.bark();  <span class="comment">// 输出：旺财汪汪叫！</span></span><br><span class="line">        cat.meow();  <span class="comment">// 输出：咪咪喵呜~</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;\n--- 类型信息 ---&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;dog 是 Dog 类实例：&quot;</span> + (dog <span class="keyword">instanceof</span> Dog));</span><br><span class="line">        System.out.println(<span class="string">&quot;dog 是 Animal 类实例：&quot;</span> + (dog <span class="keyword">instanceof</span> Animal));</span><br><span class="line">        System.out.println(<span class="string">&quot;dog 是 Cat 类实例：&quot;</span> + (dog <span class="keyword">instanceof</span> Cat));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-多态"><a href="#3-多态" class="headerlink" title="3. 多态"></a>3. 多态</h3><p><strong>多态</strong>是面向对象的三大特性之一，指的是同一个方法调用在不同对象上有不同的行为。多态让程序具有更好的扩展性和灵活性。</p><p>多态的两种形式：</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["多态"] --> B["编译时多态 方法重载 Overload"]    A --> C["运行时多态 方法重写 Override"]    B --> B1["同类中方法名相同 参数列表不同"]    C --> C1["父子类中方法签名相同 子类提供不同实现"]    style A fill:#fff3e0</pre></div><p><strong>（1）方法重载（编译时多态）</strong></p><p>同一个类中，方法名相同但参数列表不同，编译器根据参数决定调用哪个方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MathHelper</span> &#123;</span><br><span class="line">    <span class="comment">// 打印整数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;整数：&quot;</span> + num);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载：打印字符串</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(String str)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;字符串：&quot;</span> + str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载：打印小数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(<span class="type">double</span> num)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;小数：&quot;</span> + num);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载：打印多个整数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(<span class="type">int</span>... nums)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;多个整数：&quot;</span> + java.util.Arrays.toString(nums));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>（2）方法重写 + 父类引用指向子类对象（运行时多态）</strong></p><p>这是多态最典型的应用场景：父类引用可以指向子类对象，调用方法时会执行子类的实现。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PolymorphismTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// ============ 向上转型 ============</span></span><br><span class="line">        <span class="comment">// 父类引用 animal 指向子类对象 dog</span></span><br><span class="line">        <span class="comment">// 这是安全的，因为 Dog is an Animal</span></span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>(<span class="string">&quot;旺财&quot;</span>, <span class="string">&quot;金毛&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 运行时多态：调用的是 Dog 重写后的 eat()</span></span><br><span class="line">        animal.eat();</span><br><span class="line">        <span class="comment">// 输出：旺财（品种：金毛）正在吃狗粮...</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// animal.sleep() 从父类继承，行为不变</span></span><br><span class="line">        animal.sleep();</span><br><span class="line">        <span class="comment">// 输出：旺财正在睡觉...</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 注意：animal 是 Animal 类型，不能调用 Dog 独有的 bark() 方法</span></span><br><span class="line">        <span class="comment">// animal.bark();  // 编译错误！</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 向下转型 ============</span></span><br><span class="line">        <span class="comment">// 向下转型需要强制转换，并且要确保类型正确</span></span><br><span class="line">        <span class="keyword">if</span> (animal <span class="keyword">instanceof</span> Dog) &#123;</span><br><span class="line">            <span class="type">Dog</span> <span class="variable">dog</span> <span class="operator">=</span> (Dog) animal;  <span class="comment">// 强制转换为 Dog 类型</span></span><br><span class="line">            dog.bark();              <span class="comment">// 现在可以调用 Dog 独有的方法了</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 使用场景：方法参数 ============</span></span><br><span class="line">        <span class="comment">// 这是一个典型的多态应用</span></span><br><span class="line">        feedAnimal(<span class="keyword">new</span> <span class="title class_">Dog</span>(<span class="string">&quot;旺财&quot;</span>, <span class="string">&quot;金毛&quot;</span>));  <span class="comment">// 传入狗</span></span><br><span class="line">        feedAnimal(<span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">&quot;咪咪&quot;</span>, <span class="literal">true</span>));   <span class="comment">// 传入猫</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 使用场景：集合 ============</span></span><br><span class="line">        List&lt;Animal&gt; animals = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        animals.add(<span class="keyword">new</span> <span class="title class_">Dog</span>(<span class="string">&quot;狗1&quot;</span>, <span class="string">&quot;哈士奇&quot;</span>));</span><br><span class="line">        animals.add(<span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">&quot;猫1&quot;</span>, <span class="literal">false</span>));</span><br><span class="line">        animals.add(<span class="keyword">new</span> <span class="title class_">Dog</span>(<span class="string">&quot;狗2&quot;</span>, <span class="string">&quot;拉布拉多&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 遍历时体现出多态</span></span><br><span class="line">        <span class="keyword">for</span> (Animal a : animals) &#123;</span><br><span class="line">            a.eat();  <span class="comment">// 每种动物吃的方式不同</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 参数类型是父类，可以接受任意子类</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">feedAnimal</span><span class="params">(Animal animal)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;开始喂食...&quot;</span>);</span><br><span class="line">        animal.eat();  <span class="comment">// 调用的是实际类型的方法</span></span><br><span class="line">        System.out.println(<span class="string">&quot;喂食完成！\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="集合框架"><a href="#集合框架" class="headerlink" title="集合框架"></a>集合框架</h2><h3 id="为什么需要集合？"><a href="#为什么需要集合？" class="headerlink" title="为什么需要集合？"></a>为什么需要集合？</h3><p>数组是我们最基本的数据结构，但它有两个明显的限制：</p><ol><li><strong>长度固定</strong>：创建后不能改变大小</li><li><strong>类型单一</strong>：只能存储同一种类型的数据（虽然有 Object[] 但使用不便）</li></ol><p>为了解决这些问题，Java 提供了<strong>集合框架（Collection Framework）</strong>，它是一套完善的接口和类，用于存储和操作一组对象。</p><h3 id="集合框架继承体系"><a href="#集合框架继承体系" class="headerlink" title="集合框架继承体系"></a>集合框架继承体系</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["Collection 接口"] --> B["List 接口"]    A --> C["Set 接口"]    A --> D["Queue 接口"]    B --> B1["ArrayList 实现类"]    B --> B2["LinkedList 实现类"]    B --> B3["Vector 实现类"]    C --> C1["HashSet 实现类"]    C --> C2["LinkedHashSet 实现类"]    C --> C3["TreeSet 实现类"]    D --> D1["PriorityQueue 实现类"]    D --> D2["LinkedList 实现类"]    A --> E["Map 接口"]    E --> E1["HashMap 实现类"]    E --> E2["LinkedHashMap 实现类"]    E --> E3["TreeMap 实现类"]    E --> E4["Hashtable 实现类"]    style A fill:#fff3e0    style E fill:#e8f5e9    style B fill:#e3f2fd    style C fill:#fce4ec    style D fill:#fff9c4</pre></div><h3 id="List-接口-—-有序可重复"><a href="#List-接口-—-有序可重复" class="headerlink" title="List 接口 — 有序可重复"></a>List 接口 — 有序可重复</h3><p><strong>List</strong> 是一个有序的集合（也称为序列），可以精确控制每个元素的位置，通过索引访问元素，允许重复元素。</p><p><strong>ArrayList 原理</strong>：内部用数组实现，通过索引访问时非常快（O(1)），但是在中间插入或删除元素时需要移动后面所有元素，效率较低。</p><p><strong>LinkedList 原理</strong>：内部用双向链表实现，插入和删除元素很快（O(1)），但随机访问需要遍历链表，效率较低。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ListDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// ============ ArrayList ============</span></span><br><span class="line">        <span class="comment">// ArrayList 是 List 接口最常用的实现类</span></span><br><span class="line">        List&lt;String&gt; arrayList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加元素</span></span><br><span class="line">        arrayList.add(<span class="string">&quot;Apple&quot;</span>);         <span class="comment">// 尾部添加</span></span><br><span class="line">        arrayList.add(<span class="string">&quot;Banana&quot;</span>);</span><br><span class="line">        arrayList.add(<span class="string">&quot;Orange&quot;</span>);</span><br><span class="line">        arrayList.add(<span class="number">1</span>, <span class="string">&quot;Grape&quot;</span>);      <span class="comment">// 指定索引位置插入</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;ArrayList 内容：&quot;</span> + arrayList);</span><br><span class="line">        System.out.println(<span class="string">&quot;大小：&quot;</span> + arrayList.size());</span><br><span class="line">        System.out.println(<span class="string">&quot;第二个元素（索引1）：&quot;</span> + arrayList.get(<span class="number">1</span>));</span><br><span class="line">        System.out.println(<span class="string">&quot;是否包含 Banana：&quot;</span> + arrayList.contains(<span class="string">&quot;Banana&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 修改元素</span></span><br><span class="line">        arrayList.set(<span class="number">0</span>, <span class="string">&quot;RedApple&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;修改后：&quot;</span> + arrayList);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 删除元素</span></span><br><span class="line">        arrayList.remove(<span class="string">&quot;Banana&quot;</span>);     <span class="comment">// 按内容删除</span></span><br><span class="line">        arrayList.remove(<span class="number">0</span>);             <span class="comment">// 按索引删除</span></span><br><span class="line">        System.out.println(<span class="string">&quot;删除后：&quot;</span> + arrayList);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;\n--- ArrayList 适用场景 ---&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 频繁按索引访问元素&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 主要是遍历查找，少量增删&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;❌ 大量在中间位置插入/删除&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ LinkedList ============</span></span><br><span class="line">        <span class="comment">// LinkedList 同时实现了 List 和 Deque 接口</span></span><br><span class="line">        LinkedList&lt;String&gt; linkedList = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        linkedList.add(<span class="string">&quot;First&quot;</span>);</span><br><span class="line">        linkedList.addFirst(<span class="string">&quot;Zero&quot;</span>);   <span class="comment">// 头部插入</span></span><br><span class="line">        linkedList.addLast(<span class="string">&quot;Last&quot;</span>);    <span class="comment">// 尾部插入</span></span><br><span class="line">        linkedList.add(<span class="number">2</span>, <span class="string">&quot;Middle&quot;</span>);   <span class="comment">// 中间插入</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;\nLinkedList 内容：&quot;</span> + linkedList);</span><br><span class="line">        System.out.println(<span class="string">&quot;第一个：&quot;</span> + linkedList.getFirst());</span><br><span class="line">        System.out.println(<span class="string">&quot;最后一个：&quot;</span> + linkedList.getLast());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// LinkedList 作为队列使用</span></span><br><span class="line">        linkedList.poll();              <span class="comment">// 弹出头部元素</span></span><br><span class="line">        System.out.println(<span class="string">&quot;poll 后：&quot;</span> + linkedList);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;\n--- LinkedList 适用场景 ---&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 频繁在头尾插入/删除&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;✅ 作为队列、栈使用&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;❌ 随机访问（按索引查找）&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Set-接口-—-无序不重复"><a href="#Set-接口-—-无序不重复" class="headerlink" title="Set 接口 — 无序不重复"></a>Set 接口 — 无序不重复</h3><p><strong>Set</strong> 是一个不包含重复元素的集合，更精确地说，Set 中不会有两个相等的元素。适合用于去重和集合运算。</p><p><strong>HashSet 原理</strong>：基于哈希表实现，元素无序，查找&#x2F;插入&#x2F;删除效率都很高（平均 O(1)）。</p><p><strong>TreeSet 原理</strong>：基于红黑树实现，元素自动排序（需要元素可比较）。</p><p><strong>LinkedHashSet 原理</strong>：基于哈希表 + 链表实现，保持元素插入顺序。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// ============ HashSet ============</span></span><br><span class="line">        <span class="comment">// 最常用的 Set 实现类，无序不重复</span></span><br><span class="line">        Set&lt;String&gt; hashSet = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        hashSet.add(<span class="string">&quot;Java&quot;</span>);</span><br><span class="line">        hashSet.add(<span class="string">&quot;Python&quot;</span>);</span><br><span class="line">        hashSet.add(<span class="string">&quot;C++&quot;</span>);</span><br><span class="line">        hashSet.add(<span class="string">&quot;Java&quot;</span>);  <span class="comment">// 重复元素，不会添加成功</span></span><br><span class="line">        hashSet.add(<span class="literal">null</span>);     <span class="comment">// 可以存储一个 null</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;HashSet 大小：&quot;</span> + hashSet.size());  <span class="comment">// 4</span></span><br><span class="line">        System.out.println(<span class="string">&quot;HashSet 内容：&quot;</span> + hashSet);  <span class="comment">// 顺序可能每次不同</span></span><br><span class="line">        System.out.println(<span class="string">&quot;是否包含 Java：&quot;</span> + hashSet.contains(<span class="string">&quot;Java&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ TreeSet ============</span></span><br><span class="line">        <span class="comment">// 元素自动排序（自然顺序）</span></span><br><span class="line">        Set&lt;Integer&gt; treeSet = <span class="keyword">new</span> <span class="title class_">TreeSet</span>&lt;&gt;();</span><br><span class="line">        treeSet.add(<span class="number">30</span>);</span><br><span class="line">        treeSet.add(<span class="number">10</span>);</span><br><span class="line">        treeSet.add(<span class="number">20</span>);</span><br><span class="line">        treeSet.add(<span class="number">40</span>);</span><br><span class="line">        treeSet.add(<span class="number">25</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;\nTreeSet 内容：&quot;</span> + treeSet);  <span class="comment">// [10, 20, 25, 30, 40] 自动排序</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// TreeSet 支持范围查询</span></span><br><span class="line">        System.out.println(<span class="string">&quot;小于 25 的元素：&quot;</span> + treeSet.headSet(<span class="number">25</span>));</span><br><span class="line">        System.out.println(<span class="string">&quot;大于等于 25 的元素：&quot;</span> + treeSet.tailSet(<span class="number">25</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ LinkedHashSet ============</span></span><br><span class="line">        <span class="comment">// 保持插入顺序</span></span><br><span class="line">        Set&lt;String&gt; linkedHashSet = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>&lt;&gt;();</span><br><span class="line">        linkedHashSet.add(<span class="string">&quot;First&quot;</span>);</span><br><span class="line">        linkedHashSet.add(<span class="string">&quot;Second&quot;</span>);</span><br><span class="line">        linkedHashSet.add(<span class="string">&quot;Third&quot;</span>);</span><br><span class="line">        linkedHashSet.add(<span class="string">&quot;First&quot;</span>);  <span class="comment">// 重复，忽略</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;\nLinkedHashSet 内容：&quot;</span> + linkedHashSet);  <span class="comment">// [First, Second, Third]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 去重演示 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n--- 去重功能演示 ---&quot;</span>);</span><br><span class="line">        List&lt;String&gt; names = Arrays.asList(<span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;Bob&quot;</span>, <span class="string">&quot;Alice&quot;</span>, <span class="string">&quot;Charlie&quot;</span>, <span class="string">&quot;Bob&quot;</span>);</span><br><span class="line">        Set&lt;String&gt; uniqueNames = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;(names);</span><br><span class="line">        System.out.println(<span class="string">&quot;原始列表：&quot;</span> + names);</span><br><span class="line">        System.out.println(<span class="string">&quot;去重后：&quot;</span> + uniqueNames);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 集合运算 ============</span></span><br><span class="line">        Set&lt;String&gt; set1 = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;(Arrays.asList(<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>, <span class="string">&quot;D&quot;</span>));</span><br><span class="line">        Set&lt;String&gt; set2 = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;(Arrays.asList(<span class="string">&quot;C&quot;</span>, <span class="string">&quot;D&quot;</span>, <span class="string">&quot;E&quot;</span>, <span class="string">&quot;F&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 并集</span></span><br><span class="line">        Set&lt;String&gt; union = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;(set1);</span><br><span class="line">        union.addAll(set2);</span><br><span class="line">        System.out.println(<span class="string">&quot;\n并集：&quot;</span> + union);  <span class="comment">// [A, B, C, D, E, F]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 交集</span></span><br><span class="line">        Set&lt;String&gt; intersection = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;(set1);</span><br><span class="line">        intersection.retainAll(set2);</span><br><span class="line">        System.out.println(<span class="string">&quot;交集：&quot;</span> + intersection);  <span class="comment">// [C, D]</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 差集</span></span><br><span class="line">        Set&lt;String&gt; difference = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;(set1);</span><br><span class="line">        difference.removeAll(set2);</span><br><span class="line">        System.out.println(<span class="string">&quot;差集：&quot;</span> + difference);  <span class="comment">// [A, B]</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Map-接口-—-键值对"><a href="#Map-接口-—-键值对" class="headerlink" title="Map 接口 — 键值对"></a>Map 接口 — 键值对</h3><p><strong>Map</strong> 存储键值对（key-value）映射关系，其中 key 不能重复，每个 key 最多对应一个 value。Map 不是 Collection 的子接口，它是独立的接口家族。</p><p><strong>HashMap 原理</strong>：基于哈希表实现，key 无序，查找&#x2F;插入&#x2F;删除效率高。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MapDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// ============ HashMap ============</span></span><br><span class="line">        <span class="comment">// 最常用的 Map 实现类</span></span><br><span class="line">        Map&lt;String, Integer&gt; hashMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加键值对</span></span><br><span class="line">        hashMap.put(<span class="string">&quot;语文&quot;</span>, <span class="number">90</span>);</span><br><span class="line">        hashMap.put(<span class="string">&quot;数学&quot;</span>, <span class="number">95</span>);</span><br><span class="line">        hashMap.put(<span class="string">&quot;英语&quot;</span>, <span class="number">88</span>);</span><br><span class="line">        hashMap.put(<span class="string">&quot;物理&quot;</span>, <span class="number">92</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// key 重复时，value 会覆盖</span></span><br><span class="line">        hashMap.put(<span class="string">&quot;数学&quot;</span>, <span class="number">100</span>);  <span class="comment">// 数学成绩更新为 100</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;HashMap 内容：&quot;</span> + hashMap);</span><br><span class="line">        System.out.println(<span class="string">&quot;数学成绩：&quot;</span> + hashMap.get(<span class="string">&quot;数学&quot;</span>));     <span class="comment">// 100</span></span><br><span class="line">        System.out.println(<span class="string">&quot;化学成绩：&quot;</span> + hashMap.get(<span class="string">&quot;化学&quot;</span>));     <span class="comment">// null，不存在</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断操作</span></span><br><span class="line">        System.out.println(<span class="string">&quot;是否包含 key &#x27;数学&#x27;：&quot;</span> + hashMap.containsKey(<span class="string">&quot;数学&quot;</span>));</span><br><span class="line">        System.out.println(<span class="string">&quot;是否包含 value 90：&quot;</span> + hashMap.containsValue(<span class="number">90</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 删除</span></span><br><span class="line">        hashMap.remove(<span class="string">&quot;英语&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;删除后：&quot;</span> + hashMap);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 遍历方式 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n--- 遍历方式 ---&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式1：遍历 key</span></span><br><span class="line">        System.out.println(<span class="string">&quot;所有学科：&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (String subject : hashMap.keySet()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;  &quot;</span> + subject);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式2：遍历 value</span></span><br><span class="line">        System.out.println(<span class="string">&quot;所有成绩：&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Integer score : hashMap.values()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;  &quot;</span> + score);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式3：遍历键值对（最常用）</span></span><br><span class="line">        System.out.println(<span class="string">&quot;键值对：&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Map.Entry&lt;String, Integer&gt; entry : hashMap.entrySet()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;  &quot;</span> + entry.getKey() + <span class="string">&quot; = &quot;</span> + entry.getValue());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式4：Lambda 表达式（Java 8+）</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Lambda 遍历：&quot;</span>);</span><br><span class="line">        hashMap.forEach((k, v) -&gt; System.out.println(<span class="string">&quot;  &quot;</span> + k + <span class="string">&quot; -&gt; &quot;</span> + v));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 其他 Map 实现 ============</span></span><br><span class="line">        <span class="comment">// TreeMap：按键排序</span></span><br><span class="line">        Map&lt;String, Integer&gt; treeMap = <span class="keyword">new</span> <span class="title class_">TreeMap</span>&lt;&gt;(hashMap);</span><br><span class="line">        System.out.println(<span class="string">&quot;\nTreeMap（按键排序）：&quot;</span> + treeMap);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// LinkedHashMap：保持插入顺序</span></span><br><span class="line">        Map&lt;String, Integer&gt; linkedMap = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;&gt;();</span><br><span class="line">        linkedMap.put(<span class="string">&quot;first&quot;</span>, <span class="number">1</span>);</span><br><span class="line">        linkedMap.put(<span class="string">&quot;second&quot;</span>, <span class="number">2</span>);</span><br><span class="line">        linkedMap.put(<span class="string">&quot;third&quot;</span>, <span class="number">3</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;LinkedHashMap（保序）：&quot;</span> + linkedMap);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="各类集合对比"><a href="#各类集合对比" class="headerlink" title="各类集合对比"></a>各类集合对比</h3><table><thead><tr><th>集合类型</th><th>线程安全</th><th>底层数据结构</th><th>元素是否有序</th><th>适用场景</th></tr></thead><tbody><tr><td>ArrayList</td><td>❌ 否</td><td>数组</td><td>按索引</td><td>随机访问多，增删少</td></tr><tr><td>LinkedList</td><td>❌ 否</td><td>双向链表</td><td>按插入顺序</td><td>增删操作频繁</td></tr><tr><td>HashSet</td><td>❌ 否</td><td>哈希表</td><td>无序</td><td>去重，不关心顺序</td></tr><tr><td>LinkedHashSet</td><td>❌ 否</td><td>哈希表+链表</td><td>插入顺序</td><td>去重且需保持顺序</td></tr><tr><td>TreeSet</td><td>❌ 否</td><td>红黑树</td><td>自然排序</td><td>需要排序的去重</td></tr><tr><td>HashMap</td><td>❌ 否</td><td>哈希表</td><td>无序</td><td>键值对存储（最常用）</td></tr><tr><td>LinkedHashMap</td><td>❌ 否</td><td>哈希表+链表</td><td>插入顺序</td><td>需要按插入顺序遍历</td></tr><tr><td>TreeMap</td><td>❌ 否</td><td>红黑树</td><td>按 key 排序</td><td>需要按键排序的映射</td></tr><tr><td>Hashtable</td><td>✅ 是</td><td>哈希表</td><td>无序</td><td>旧版本，现已被 ConcurrentHashMap 取代</td></tr><tr><td>ConcurrentHashMap</td><td>✅ 是</td><td>分段锁哈希表</td><td>无序</td><td>高并发场景</td></tr></tbody></table><hr><h2 id="多线程基础"><a href="#多线程基础" class="headerlink" title="多线程基础"></a>多线程基础</h2><h3 id="什么是线程？"><a href="#什么是线程？" class="headerlink" title="什么是线程？"></a>什么是线程？</h3><p><strong>进程（Process）</strong> 是程序的一次执行过程，是系统分配资源的基本单位。每个进程都有独立的内存空间。</p><p><strong>线程（Thread）</strong> 是进程中的一个执行单元，是 CPU 调度的最小单位。一个进程可以包含多个线程，这些线程共享进程的内存空间。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["进程"] --> B["内存空间 堆、方法区"]    A --> C["线程1"]    A --> D["线程2"]    A --> E["线程3"]    B --> F["共享数据"]    C --> G["栈1"]    D --> H["栈2"]    E --> I["栈3"]    style A fill:#fff3e0    style B fill:#e8f5e9    style C fill:#e3f2fd    style D fill:#e3f2fd    style E fill:#e3f2fd</pre></div><p><strong>为什么使用多线程？</strong></p><ul><li><strong>提高效率</strong>：多核 CPU 可以真正并行执行多个任务</li><li><strong>阻塞不影响</strong>：一个线程阻塞时，其他线程可以继续执行</li><li><strong>提升响应</strong>：可以将耗时操作放到后台，主线程保持响应</li></ul><h3 id="线程生命周期"><a href="#线程生命周期" class="headerlink" title="线程生命周期"></a>线程生命周期</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>stateDiagram-v2    ["*"] --> 新建: new Thread()    新建 --> 就绪: start() 被调用    就绪 --> 运行: 获得CPU执行权    运行 --> 就绪: yield() 时间片用完    运行 --> 阻塞: wait() sleep() join() I/O阻塞    阻塞 --> 就绪: notify() notifyAll() 时间到 join结束    运行 --> 死亡: run()执行完毕 未捕获异常    死亡 --> ["*"]</pre></div><p><strong>各状态说明：</strong></p><table><thead><tr><th>状态</th><th>含义</th></tr></thead><tbody><tr><td><strong>New（新建）</strong></td><td>创建了线程对象，但还没调用 start()</td></tr><tr><td><strong>Runnable（就绪）</strong></td><td>调用了 start()，等待 CPU 分配时间片</td></tr><tr><td><strong>Running（运行）</strong></td><td>获得了 CPU 执行权，正在执行 run()</td></tr><tr><td><strong>Blocked（阻塞）</strong></td><td>等待获取锁、sleep、wait 等</td></tr><tr><td><strong>Dead（死亡）</strong></td><td>run() 执行完毕或抛出未捕获异常</td></tr></tbody></table><h3 id="创建线程的方式"><a href="#创建线程的方式" class="headerlink" title="创建线程的方式"></a>创建线程的方式</h3><p>Java 中有三种创建线程的方式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ============ 方式一：继承 Thread 类 ============</span></span><br><span class="line"><span class="comment">// 缺点：Java 是单继承，继承了 Thread 就不能再继承其他类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 线程要执行的代码</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;线程1 - 第 &quot;</span> + i + <span class="string">&quot; 次执行&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">100</span>);  <span class="comment">// 休眠 100 毫秒</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 方式二：实现 Runnable 接口（推荐） ============</span></span><br><span class="line"><span class="comment">// 优点：只是实现了接口，还可以继承其他类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;线程2 - 第 &quot;</span> + i + <span class="string">&quot; 次执行&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">100</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 方式三：实现 Callable 接口 + FutureTask ============</span></span><br><span class="line"><span class="comment">// 优点：有返回值，可以抛出异常，可以取消任务</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyCallable</span> <span class="keyword">implements</span> <span class="title class_">Callable</span>&lt;Integer&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">            sum += i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 模拟耗时操作</span></span><br><span class="line">        Thread.sleep(<span class="number">500</span>);</span><br><span class="line">        <span class="keyword">return</span> sum;  <span class="comment">// 返回结果</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 测试类 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadCreateDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 方式一</span></span><br><span class="line">        <span class="type">MyThread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br><span class="line">        thread1.start();  <span class="comment">// 注意：必须调用 start()，不是 run()</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式二</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">MyRunnable</span>());</span><br><span class="line">        thread2.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式三</span></span><br><span class="line">        FutureTask&lt;Integer&gt; futureTask = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">MyCallable</span>());</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(futureTask);</span><br><span class="line">        thread3.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取 Callable 的返回值</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">result</span> <span class="operator">=</span> futureTask.get();  <span class="comment">// 会阻塞等待结果</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Callable 计算结果：&quot;</span> + result);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待所有线程结束</span></span><br><span class="line">        thread1.join();</span><br><span class="line">        thread2.join();</span><br><span class="line">        thread3.join();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;所有线程执行完毕！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="线程同步"><a href="#线程同步" class="headerlink" title="线程同步"></a>线程同步</h3><p>多线程带来了效率的提升，但也带来了新的问题：<strong>线程安全问题</strong>。</p><p>当多个线程同时访问同一个资源（变量、文件、数据库等）时，可能会出现数据不一致的问题。</p><p><strong>经典的线程不安全问题示例：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 模拟抢票系统</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TicketSeller</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tickets</span> <span class="operator">=</span> <span class="number">100</span>;  <span class="comment">// 100 张票</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (tickets &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 模拟出票耗时</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">1</span>);  <span class="comment">// 休眠 1 毫秒</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 关键问题：多个线程可能同时读到 tickets &gt; 0</span></span><br><span class="line">            <span class="comment">// 导致卖出同一张票，甚至卖出负数票</span></span><br><span class="line">            System.out.println(Thread.currentThread().getName()</span><br><span class="line">                    + <span class="string">&quot; 卖出第 &quot;</span> + tickets + <span class="string">&quot; 张票&quot;</span>);</span><br><span class="line">            tickets--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">TicketSeller</span> <span class="variable">seller</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TicketSeller</span>();</span><br><span class="line">        <span class="comment">// 创建 10 个线程同时卖票</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(seller, <span class="string">&quot;窗口&quot;</span> + (i + <span class="number">1</span>)).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>解决方案：使用 synchronized 关键字</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SafeTicketSeller</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tickets</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 同步方法：整个方法体被加锁</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">sellTicket</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (tickets &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName()</span><br><span class="line">                    + <span class="string">&quot; 卖出第 &quot;</span> + tickets + <span class="string">&quot; 张票&quot;</span>);</span><br><span class="line">            tickets--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (tickets &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            sellTicket();  <span class="comment">// 调用同步方法</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SafeTicketSeller</span> <span class="variable">seller</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SafeTicketSeller</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(seller, <span class="string">&quot;窗口&quot;</span> + (i + <span class="number">1</span>)).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>synchronized 同步原理：</strong></p><ul><li>每个对象都有一把<strong>内部锁（Monitor Lock）</strong></li><li>当线程进入 <code>synchronized</code> 方法时，会自动获取该对象的锁</li><li>其他线程尝试进入时会被阻塞，直到锁被释放</li><li>当方法执行完毕（正常或异常），锁会自动释放</li></ul><h3 id="生产者消费者问题"><a href="#生产者消费者问题" class="headerlink" title="生产者消费者问题"></a>生产者消费者问题</h3><p>这是多线程经典问题：生产者生产数据，消费者消费数据，两者需要协调进行。</p><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["生产者"] -->|放入数据| B["缓冲区"]    B -->|取出数据| C["消费者"]    style B fill:#fff3e0</pre></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProducerConsumer</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">CAPACITY</span> <span class="operator">=</span> <span class="number">10</span>;  <span class="comment">// 缓冲区容量</span></span><br><span class="line">    <span class="keyword">private</span> Queue&lt;Integer&gt; buffer = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 生产者方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">produce</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">while</span> (buffer.size() &gt;= CAPACITY) &#123;</span><br><span class="line">            <span class="comment">// 缓冲区满了，生产者等待</span></span><br><span class="line">            System.out.println(<span class="string">&quot;缓冲区已满，生产者等待...&quot;</span>);</span><br><span class="line">            wait();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        count++;</span><br><span class="line">        buffer.offer(count);</span><br><span class="line">        System.out.println(<span class="string">&quot;生产者放入第 &quot;</span> + count + <span class="string">&quot; 个产品&quot;</span></span><br><span class="line">                + <span class="string">&quot;，缓冲区大小：&quot;</span> + buffer.size());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 唤醒消费者</span></span><br><span class="line">        notifyAll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 消费者方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">consume</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">while</span> (buffer.isEmpty()) &#123;</span><br><span class="line">            <span class="comment">// 缓冲区空，消费者等待</span></span><br><span class="line">            System.out.println(<span class="string">&quot;缓冲区为空，消费者等待...&quot;</span>);</span><br><span class="line">            wait();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> <span class="variable">product</span> <span class="operator">=</span> buffer.poll();</span><br><span class="line">        System.out.println(<span class="string">&quot;消费者取出第 &quot;</span> + product + <span class="string">&quot; 个产品&quot;</span></span><br><span class="line">                + <span class="string">&quot;，缓冲区大小：&quot;</span> + buffer.size());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 唤醒生产者</span></span><br><span class="line">        notifyAll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ProducerConsumer</span> <span class="variable">pc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ProducerConsumer</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 生产者线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">producer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">20</span>; i++) &#123;</span><br><span class="line">                    pc.produce();</span><br><span class="line">                    Thread.sleep((<span class="type">long</span>) (Math.random() * <span class="number">500</span>));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;生产者&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 消费者线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">consumer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">20</span>; i++) &#123;</span><br><span class="line">                    pc.consume();</span><br><span class="line">                    Thread.sleep((<span class="type">long</span>) (Math.random() * <span class="number">800</span>));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;消费者&quot;</span>);</span><br><span class="line"></span><br><span class="line">        producer.start();</span><br><span class="line">        consumer.start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            producer.join();</span><br><span class="line">            consumer.join();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;所有产品已处理完毕！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="I-O-流"><a href="#I-O-流" class="headerlink" title="I&#x2F;O 流"></a>I&#x2F;O 流</h2><h3 id="什么是-I-O？"><a href="#什么是-I-O？" class="headerlink" title="什么是 I&#x2F;O？"></a>什么是 I&#x2F;O？</h3><p><strong>I&#x2F;O</strong> 是 Input&#x2F;Output 的缩写，即输入输出。在 Java 中，I&#x2F;O 用于程序与外部数据源之间的数据交换，包括：</p><ul><li><strong>读取文件</strong>（Input）</li><li><strong>写入文件</strong>（Output）</li><li><strong>网络通信</strong></li><li><strong>标准输入输出</strong></li></ul><h3 id="I-O-分类"><a href="#I-O-分类" class="headerlink" title="I&#x2F;O 分类"></a>I&#x2F;O 分类</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart TD    A["I/O 流"] --> B["字节流"]    A --> C["字符流"]    B --> B1["InputStream 字节输入"]    B --> B2["OutputStream 字节输出"]    C --> C1["Reader 字符输入"]    C --> C2["Writer 字符输出"]    B1 --> B11["FileInputStream"]    B1 --> B12["BufferedInputStream"]    B1 --> B13["ObjectInputStream"]    B2 --> B21["FileOutputStream"]    B2 --> B22["BufferedOutputStream"]    B2 --> B23["ObjectOutputStream"]    C1 --> C11["FileReader"]    C1 --> C12["BufferedReader"]    C2 --> C21["FileWriter"]    C2 --> C22["BufferedWriter"]    style A fill:#fff3e0</pre></div><p><strong>何时用字节流，何时用字符流？</strong></p><ul><li><strong>字节流</strong>：用于处理二进制数据，如图片、音频、视频、压缩文件等</li><li><strong>字符流</strong>：用于处理文本数据，按字符读取，更适合处理文字</li></ul><h3 id="文件读写示例"><a href="#文件读写示例" class="headerlink" title="文件读写示例"></a>文件读写示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FileIODemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 字节流：复制图片 ============</span></span><br><span class="line">    <span class="comment">// 适用于二进制文件（图片、音视频等）</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">copyImage</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">source</span> <span class="operator">=</span> <span class="string">&quot;source.jpg&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">dest</span> <span class="operator">=</span> <span class="string">&quot;dest.jpg&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// try-with-resources：自动关闭流</span></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(source);</span><br><span class="line">             <span class="type">OutputStream</span> <span class="variable">os</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(dest)) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 使用缓冲数组提高效率</span></span><br><span class="line">            <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">8192</span>];  <span class="comment">// 8KB 缓冲区</span></span><br><span class="line">            <span class="type">int</span> bytesRead;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 循环读取直到文件结束（read 返回 -1 表示读完）</span></span><br><span class="line">            <span class="keyword">while</span> ((bytesRead = is.read(buffer)) != -<span class="number">1</span>) &#123;</span><br><span class="line">                os.write(buffer, <span class="number">0</span>, bytesRead);  <span class="comment">// 只写入实际读取的字节数</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;图片复制完成！&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;耗时：&quot;</span> + (end - start) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 字符流：读写文本文件 ============</span></span><br><span class="line">    <span class="comment">// 适用于纯文本文件</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">readWriteText</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">inputFile</span> <span class="operator">=</span> <span class="string">&quot;input.txt&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">outputFile</span> <span class="operator">=</span> <span class="string">&quot;output.txt&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 基本字符流</span></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Reader</span> <span class="variable">reader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileReader</span>(inputFile);</span><br><span class="line">             <span class="type">Writer</span> <span class="variable">writer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileWriter</span>(outputFile)) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">char</span>[] buffer = <span class="keyword">new</span> <span class="title class_">char</span>[<span class="number">1024</span>];</span><br><span class="line">            <span class="type">int</span> length;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> ((length = reader.read(buffer)) != -<span class="number">1</span>) &#123;</span><br><span class="line">                writer.write(buffer, <span class="number">0</span>, length);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;文本文件复制完成！&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ BufferedReader：按行读取（推荐） ============</span></span><br><span class="line">    <span class="comment">// BufferedReader 提供了 readLine() 方法，按行读取更方便</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">readByLine</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">file</span> <span class="operator">=</span> <span class="string">&quot;log.txt&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">BufferedReader</span> <span class="variable">br</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">FileReader</span>(file), <span class="number">8192</span>)) &#123;  <span class="comment">// 指定缓冲区大小</span></span><br><span class="line"></span><br><span class="line">            String line;</span><br><span class="line">            <span class="type">int</span> <span class="variable">lineNumber</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// readLine() 返回 null 表示文件结束</span></span><br><span class="line">            <span class="keyword">while</span> ((line = br.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">                lineNumber++;</span><br><span class="line">                System.out.println(lineNumber + <span class="string">&quot;: &quot;</span> + line);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ BufferedWriter：带缓冲写入 ============</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">bufferedWrite</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">file</span> <span class="operator">=</span> <span class="string">&quot;output.txt&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">BufferedWriter</span> <span class="variable">bw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedWriter</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">FileWriter</span>(file))) &#123;</span><br><span class="line"></span><br><span class="line">            bw.write(<span class="string">&quot;第一行内容&quot;</span>);</span><br><span class="line">            bw.newLine();  <span class="comment">// 写入换行符</span></span><br><span class="line"></span><br><span class="line">            bw.write(<span class="string">&quot;第二行内容&quot;</span>);</span><br><span class="line">            bw.newLine();</span><br><span class="line"></span><br><span class="line">            bw.write(<span class="string">&quot;第三行内容&quot;</span>);</span><br><span class="line"></span><br><span class="line">            bw.flush();  <span class="comment">// 刷新缓冲区，将数据立即写入文件</span></span><br><span class="line">            System.out.println(<span class="string">&quot;写入完成！&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ Scanner：简单场景首选 ============</span></span><br><span class="line">    <span class="comment">// 适合小文件的简单读取</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">scannerRead</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">file</span> <span class="operator">=</span> <span class="string">&quot;data.txt&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(<span class="keyword">new</span> <span class="title class_">File</span>(file))) &#123;</span><br><span class="line">            scanner.useDelimiter(<span class="string">&quot;,&quot;</span>);  <span class="comment">// 指定分隔符</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> (scanner.hasNext()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (scanner.hasNextInt()) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;整数：&quot;</span> + scanner.nextInt());</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (scanner.hasNextDouble()) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;小数：&quot;</span> + scanner.nextDouble());</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;字符串：&quot;</span> + scanner.next());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (FileNotFoundException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;文件不存在：&quot;</span> + e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="对象序列化"><a href="#对象序列化" class="headerlink" title="对象序列化"></a>对象序列化</h3><p><strong>序列化</strong>是将对象转换为字节序列的过程，用于将对象保存到文件或在网络上传输。<strong>反序列化</strong>是序列化的逆过程。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ============ 可序列化类 ============</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">    <span class="comment">// serialVersionUID：版本号，序列化/反序列化时需要匹配</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// transient 修饰的字段不会被序列化</span></span><br><span class="line">    <span class="comment">// 常用于敏感信息（密码等）或不需要持久化的数据</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> String password;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name, <span class="type">int</span> age, String password)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        <span class="built_in">this</span>.password = password;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Person&#123;name=&#x27;&quot;</span> + name + <span class="string">&quot;&#x27;, age=&quot;</span> + age</span><br><span class="line">                + <span class="string">&quot;, password=&#x27;&quot;</span> + password + <span class="string">&quot;&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 序列化与反序列化 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SerializationDemo</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">FILE</span> <span class="operator">=</span> <span class="string">&quot;person.dat&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 序列化：对象 -&gt; 文件</span></span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">&quot;张三&quot;</span>, <span class="number">25</span>, <span class="string">&quot;123456&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(FILE))) &#123;</span><br><span class="line">            oos.writeObject(person);</span><br><span class="line">            System.out.println(<span class="string">&quot;序列化成功！对象：&quot;</span> + person);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 反序列化：文件 -&gt; 对象</span></span><br><span class="line">        <span class="keyword">try</span> (<span class="type">ObjectInputStream</span> <span class="variable">ois</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(FILE))) &#123;</span><br><span class="line">            <span class="type">Person</span> <span class="variable">p</span> <span class="operator">=</span> (Person) ois.readObject();</span><br><span class="line">            System.out.println(<span class="string">&quot;反序列化成功！对象：&quot;</span> + p);</span><br><span class="line">            <span class="comment">// 注意：password 会是 null，因为被 transient 修饰</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException | ClassNotFoundException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h2><h3 id="什么是泛型？"><a href="#什么是泛型？" class="headerlink" title="什么是泛型？"></a>什么是泛型？</h3><p><strong>泛型</strong>是 Java 5 引入的一个新特性，它的本质是<strong>参数化类型</strong>。我们可以把泛型理解成一种”类型的占位符”，在编写代码时不指定具体类型，而在使用时再确定类型。</p><p><strong>为什么需要泛型？</strong></p><p>举一个实际例子：假设我们需要一个容器来装东西。</p><p><strong>没有泛型时（使用 Object）：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用 Object 类型的容器</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ObjectBox</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Object content;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(Object content)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.content = content;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> content;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用时需要强制类型转换，容易出错</span></span><br><span class="line"><span class="type">ObjectBox</span> <span class="variable">box</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectBox</span>();</span><br><span class="line">box.set(<span class="string">&quot;Hello&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> (String) box.get();  <span class="comment">// 必须强制转换</span></span><br><span class="line"></span><br><span class="line">box.set(<span class="number">123</span>);  <span class="comment">// 可以放入任何类型</span></span><br><span class="line"><span class="type">Integer</span> <span class="variable">num</span> <span class="operator">=</span> (Integer) box.get();  <span class="comment">// 如果之前放的是 String，这里会报错</span></span><br></pre></td></tr></table></figure><p><strong>有泛型时：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用泛型的容器</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GenericBox</span>&lt;T&gt; &#123;  <span class="comment">// T 是类型参数</span></span><br><span class="line">    <span class="keyword">private</span> T content;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(T content)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.content = content;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> content;  <span class="comment">// 无需强制转换</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用时指定具体类型</span></span><br><span class="line">GenericBox&lt;String&gt; stringBox = <span class="keyword">new</span> <span class="title class_">GenericBox</span>&lt;&gt;();</span><br><span class="line">stringBox.set(<span class="string">&quot;Hello&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> stringBox.get();  <span class="comment">// 无需强制转换，类型安全</span></span><br><span class="line"></span><br><span class="line">GenericBox&lt;Integer&gt; intBox = <span class="keyword">new</span> <span class="title class_">GenericBox</span>&lt;&gt;();</span><br><span class="line">intBox.set(<span class="number">123</span>);</span><br><span class="line"><span class="type">Integer</span> <span class="variable">num</span> <span class="operator">=</span> intBox.get();  <span class="comment">// 无需强制转换</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 编译阶段就会报错，无法放入错误类型</span></span><br><span class="line"><span class="comment">// stringBox.set(123);  // 编译错误！</span></span><br></pre></td></tr></table></figure><p><strong>泛型的优势：</strong></p><ol><li><strong>类型安全</strong>：编译时检查类型，防止 ClassCastException</li><li><strong>消除强制类型转换</strong>：代码更简洁</li><li><strong>代码复用</strong>：一套代码可以处理多种类型</li></ol><h3 id="泛型体系"><a href="#泛型体系" class="headerlink" title="泛型体系"></a>泛型体系</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["泛型"] --> B["泛型类 类定义时使用泛型"]    A --> C["泛型接口 接口定义时使用泛型"]    A --> D["泛型方法 方法定义时使用泛型"]    A --> E["泛型通配符 灵活的类型限定"]    style A fill:#fff3e0</pre></div><h3 id="泛型类与泛型接口"><a href="#泛型类与泛型接口" class="headerlink" title="泛型类与泛型接口"></a>泛型类与泛型接口</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ============ 泛型类 ============</span></span><br><span class="line"><span class="comment">// Box&lt;T&gt; 中的 T 被称为类型参数（Type Parameter）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Box</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> T content;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(T content)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.content = content;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> content;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 多个类型参数</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Pair</span>&lt;K, V&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> K key;</span><br><span class="line">    <span class="keyword">private</span> V value;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Pair</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.key = key;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> K <span class="title function_">getKey</span><span class="params">()</span> &#123; <span class="keyword">return</span> key; &#125;</span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">getValue</span><span class="params">()</span> &#123; <span class="keyword">return</span> value; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 泛型接口 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Container</span>&lt;K, V&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(K key, V value)</span>;</span><br><span class="line">    V <span class="title function_">get</span><span class="params">(K key)</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">contains</span><span class="params">(K key)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实现泛型接口时，可以指定具体类型</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StringIntegerContainer</span> <span class="keyword">implements</span> <span class="title class_">Container</span>&lt;String, Integer&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(String key, Integer value)</span> &#123;</span><br><span class="line">        map.put(key, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">get</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> map.get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">contains</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> map.containsKey(key);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 也可以继续使用泛型</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GenericContainer</span>&lt;K, V&gt; <span class="keyword">implements</span> <span class="title class_">Container</span>&lt;K, V&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;K, V&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">        map.put(key, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">get</span><span class="params">(K key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> map.get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">contains</span><span class="params">(K key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> map.containsKey(key);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ============ 使用示例 ============</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GenericDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 泛型类使用</span></span><br><span class="line">        Box&lt;String&gt; stringBox = <span class="keyword">new</span> <span class="title class_">Box</span>&lt;&gt;();</span><br><span class="line">        stringBox.set(<span class="string">&quot;Hello&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Box 内容：&quot;</span> + stringBox.get());</span><br><span class="line"></span><br><span class="line">        Box&lt;Integer&gt; intBox = <span class="keyword">new</span> <span class="title class_">Box</span>&lt;&gt;();</span><br><span class="line">        intBox.set(<span class="number">123</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Box 内容：&quot;</span> + intBox.get());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 键值对使用</span></span><br><span class="line">        Pair&lt;String, Integer&gt; pair = <span class="keyword">new</span> <span class="title class_">Pair</span>&lt;&gt;(<span class="string">&quot;语文&quot;</span>, <span class="number">90</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;键值对：&quot;</span> + pair.getKey() + <span class="string">&quot; = &quot;</span> + pair.getValue());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 泛型接口使用</span></span><br><span class="line">        Container&lt;String, Integer&gt; container = <span class="keyword">new</span> <span class="title class_">StringIntegerContainer</span>();</span><br><span class="line">        container.put(<span class="string">&quot;数学&quot;</span>, <span class="number">95</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;容器内容：&quot;</span> + container.get(<span class="string">&quot;数学&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="泛型方法"><a href="#泛型方法" class="headerlink" title="泛型方法"></a>泛型方法</h3><p>泛型方法可以在普通类中定义，不需要类本身是泛型的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GenericMethodDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 基本泛型方法 ============</span></span><br><span class="line">    <span class="comment">// &lt;T&gt; 表示这是一个泛型方法，T 是类型参数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">printArray</span><span class="params">(T[] array)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (T element : array) &#123;</span><br><span class="line">            System.out.print(element + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 返回类型也是泛型 ============</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="type">int</span> <span class="title function_">findIndex</span><span class="params">(T[] array, T target)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; array.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (array[i].equals(target)) &#123;</span><br><span class="line">                <span class="keyword">return</span> i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 多个类型参数 ============</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;K, V&gt; Map&lt;K, V&gt; <span class="title function_">createMap</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">        Map&lt;K, V&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(key, value);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 限制类型上限 ============</span></span><br><span class="line">    <span class="comment">// &lt;T extends Number&gt; 表示 T 必须是 Number 或其子类</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T <span class="keyword">extends</span> <span class="title class_">Number</span>&gt; <span class="type">double</span> <span class="title function_">sum</span><span class="params">(T a, T b)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> a.doubleValue() + b.doubleValue();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ 比较器示例 ============</span></span><br><span class="line">    <span class="comment">// &lt;T extends Comparable&lt;T&gt;&gt; 确保 T 可以比较</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T <span class="keyword">extends</span> <span class="title class_">Comparable</span>&lt;T&gt;&gt; T <span class="title function_">findMax</span><span class="params">(T a, T b)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (a.compareTo(b) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> a;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> b;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        Integer[] intArray = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">        String[] strArray = &#123;<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>&#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 泛型方法调用</span></span><br><span class="line">        printArray(intArray);  <span class="comment">// 输出：1 2 3 4 5</span></span><br><span class="line">        printArray(strArray);  <span class="comment">// 输出：A B C</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 查找索引</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> findIndex(intArray, <span class="number">3</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;3 在数组中的索引：&quot;</span> + index);  <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建 Map</span></span><br><span class="line">        Map&lt;String, Integer&gt; map = createMap(<span class="string">&quot;年龄&quot;</span>, <span class="number">25</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Map：&quot;</span> + map);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 类型限制</span></span><br><span class="line">        System.out.println(<span class="string">&quot;求和：&quot;</span> + sum(<span class="number">1</span>, <span class="number">2.5</span>));  <span class="comment">// 3.5</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 最大值</span></span><br><span class="line">        System.out.println(<span class="string">&quot;最大值：&quot;</span> + findMax(<span class="number">10</span>, <span class="number">20</span>));  <span class="comment">// 20</span></span><br><span class="line">        System.out.println(<span class="string">&quot;最大值：&quot;</span> + findMax(<span class="string">&quot;apple&quot;</span>, <span class="string">&quot;banana&quot;</span>));  <span class="comment">// banana</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="泛型通配符"><a href="#泛型通配符" class="headerlink" title="泛型通配符"></a>泛型通配符</h3><p>泛型通配符用于泛型方法的参数中，提供更灵活的类型处理。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WildcardDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ &lt;? extends T&gt; 上限通配符 ============</span></span><br><span class="line">    <span class="comment">// 表示未知类型是 T 或 T 的子类，只能读取（作为生产者）</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">printNumbers</span><span class="params">(List&lt;? extends Number&gt; list)</span> &#123;</span><br><span class="line">        <span class="comment">// 可以读取，但类型未知</span></span><br><span class="line">        <span class="keyword">for</span> (Number num : list) &#123;</span><br><span class="line">            System.out.println(num + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 不能写入任何东西！因为不知道具体是 Integer 还是 Double</span></span><br><span class="line">        <span class="comment">// list.add(1);  // 编译错误！</span></span><br><span class="line">        <span class="comment">// list.add(1.0); // 编译错误！</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ &lt;? super T&gt; 下限通配符 ============</span></span><br><span class="line">    <span class="comment">// 表示未知类型是 T 或 T 的父类，只能写入（作为消费者）</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">addNumbers</span><span class="params">(List&lt;? <span class="built_in">super</span> Integer&gt; list)</span> &#123;</span><br><span class="line">        <span class="comment">// 可以写入 Integer 或其子类</span></span><br><span class="line">        list.add(<span class="number">1</span>);</span><br><span class="line">        list.add(<span class="number">2</span>);</span><br><span class="line">        list.add(Integer.valueOf(<span class="number">3</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 读取时只能当 Object 处理</span></span><br><span class="line">        <span class="comment">// Number num = list.get(0);  // 编译错误！</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> list.get(<span class="number">0</span>);  <span class="comment">// 只能当 Object 处理</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============ &lt;?&gt; 无界通配符 ============</span></span><br><span class="line">    <span class="comment">// 表示未知类型，可以读写，但类型不安全需要自己注意</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">printAnyList</span><span class="params">(List&lt;?&gt; list)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (Object obj : list) &#123;</span><br><span class="line">            System.out.println(obj);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 可以写入 null</span></span><br><span class="line">        <span class="comment">// list.add(&quot;string&quot;);  // 编译错误！</span></span><br><span class="line">        list.add(<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 读取时只能当 Object 处理</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">element</span> <span class="operator">=</span> list.get(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 准备测试数据</span></span><br><span class="line">        List&lt;Integer&gt; intList = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line">        List&lt;Double&gt; doubleList = Arrays.asList(<span class="number">1.1</span>, <span class="number">2.2</span>, <span class="number">3.3</span>);</span><br><span class="line">        List&lt;Number&gt; numberList = Arrays.asList(<span class="number">1</span>, <span class="number">2.0</span>, <span class="number">3L</span>);</span><br><span class="line">        List&lt;Object&gt; objectList = Arrays.asList(<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;c&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// &lt;? extends Number&gt; 可以接受 Integer、Double、Number</span></span><br><span class="line">        System.out.println(<span class="string">&quot;=== printNumbers ===&quot;</span>);</span><br><span class="line">        printNumbers(intList);</span><br><span class="line">        printNumbers(doubleList);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// &lt;? super Integer&gt; 可以接受 Integer、Number、Object</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== addNumbers ===&quot;</span>);</span><br><span class="line">        addNumbers(intList);</span><br><span class="line">        addNumbers(numberList);</span><br><span class="line">        System.out.println(<span class="string">&quot;添加后的 Integer 列表：&quot;</span> + intList);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// &lt;?&gt; 可以接受任何类型</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== printAnyList ===&quot;</span>);</span><br><span class="line">        printAnyList(intList);</span><br><span class="line">        printAnyList(objectList);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>通配符记忆口诀：</strong></p><ul><li><strong><code>&lt;? extends T&gt;</code></strong> — “我是 T 的消费者，只能读，不能写”（Producer Extends）</li><li><strong><code>&lt;? super T&gt;</code></strong> — “我是 T 的生产者，只能写，不能读”（Consumer Super）</li></ul><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["泛型通配符"] --> B["extends T 上限 只读"]    A --> C["super T 下限 只写"]    A --> D["无界 ? 读写 Object"]    style A fill:#fff3e0</pre></div><hr><h2 id="反射机制"><a href="#反射机制" class="headerlink" title="反射机制"></a>反射机制</h2><h3 id="什么是反射？"><a href="#什么是反射？" class="headerlink" title="什么是反射？"></a>什么是反射？</h3><p><strong>反射（Reflection）</strong> 是 Java 的一个强大特性，它允许程序在<strong>运行时</strong>动态地获取类的信息、创建对象、调用方法和访问属性。</p><p>正常情况下，我们在编译时就知道要使用哪个类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;hello&quot;</span>);  <span class="comment">// 编译时就知道是 String</span></span><br><span class="line">str.substring(<span class="number">0</span>, <span class="number">2</span>);               <span class="comment">// 编译时就知道有 substring 方法</span></span><br></pre></td></tr></table></figure><p>但有些场景下，<strong>我们在编译时不知道要操作哪个类</strong>，比如：</p><ul><li>开发框架（Spring、Hibernate）需要在运行时加载类</li><li>注解处理器需要在运行时读取注解信息</li><li>动态代理、RPC 框架等</li></ul><p><strong>反射就是在程序运行时自我探索和操作的能力。</strong></p><h3 id="反射核心-API"><a href="#反射核心-API" class="headerlink" title="反射核心 API"></a>反射核心 API</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>flowchart LR    A["反射 API"] --> B["Class 类本身"]    A --> C["Field 属性"]    A --> D["Method 方法"]    A --> E["Constructor 构造方法"]    B --> B1["getName"]    B --> B2["newInstance"]    B --> B3["getDeclaredFields"]    C --> C1["get"]    C --> C2["set"]    C --> C3["setAccessible"]    D --> D1["invoke"]    D --> D2["getParameterTypes"]    style A fill:#fff3e0</pre></div><h3 id="获取-Class-对象"><a href="#获取-Class-对象" class="headerlink" title="获取 Class 对象"></a>获取 Class 对象</h3><p>Class 对象是反射的入口，每个类在 JVM 中都只有一个 Class 对象。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectDemo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;Hello&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式一：使用 getClass() 方法</span></span><br><span class="line">        <span class="comment">// 任何对象都有 getClass() 方法</span></span><br><span class="line">        Class&lt;?&gt; c1 = str.getClass();</span><br><span class="line">        System.out.println(<span class="string">&quot;方式一获取：&quot;</span> + c1.getName());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式二：使用 Class.forName()</span></span><br><span class="line">        <span class="comment">// 最常用，可以动态指定类名</span></span><br><span class="line">        Class&lt;?&gt; c2 = Class.forName(<span class="string">&quot;java.lang.String&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;方式二获取：&quot;</span> + c2.getName());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式三：使用 .class 属性</span></span><br><span class="line">        <span class="comment">// 最简单直接，适用于已知类型的情况</span></span><br><span class="line">        Class&lt;?&gt; c3 = String.class;</span><br><span class="line">        System.out.println(<span class="string">&quot;方式三获取：&quot;</span> + c3.getName());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式四：基本类型的 TYPE</span></span><br><span class="line">        <span class="comment">// 每个基本类型都有一个包装类，包装类有 TYPE 属性</span></span><br><span class="line">        Class&lt;?&gt; c4 = Integer.TYPE;</span><br><span class="line">        System.out.println(<span class="string">&quot;Integer.TYPE：&quot;</span> + c4);  <span class="comment">// int</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 验证：同一个类的 Class 对象是同一个</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 验证 Class 唯一性 ===&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;c1 == c2：&quot;</span> + (c1 == c2));  <span class="comment">// true</span></span><br><span class="line">        System.out.println(<span class="string">&quot;c2 == c3：&quot;</span> + (c2 == c3));  <span class="comment">// true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="操作类的结构"><a href="#操作类的结构" class="headerlink" title="操作类的结构"></a>操作类的结构</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 示例类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String name;</span><br><span class="line">    <span class="keyword">protected</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name, <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHello</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;你好，我是&quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">privateMethod</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;这是私有方法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectStructure</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        Class&lt;?&gt; clazz = Person.class;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 获取类信息 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;=== 类信息 ===&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;完整类名：&quot;</span> + clazz.getName());        <span class="comment">// 包名.类名</span></span><br><span class="line">        System.out.println(<span class="string">&quot;简单类名：&quot;</span> + clazz.getSimpleName());   <span class="comment">// 只有类名</span></span><br><span class="line">        System.out.println(<span class="string">&quot;包名：&quot;</span> + clazz.getPackage());          <span class="comment">// 所在包</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 获取属性 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 属性信息 ===&quot;</span>);</span><br><span class="line">        <span class="comment">// getDeclaredFields() 获取所有声明的属性（包括私有）</span></span><br><span class="line">        <span class="keyword">for</span> (Field field : clazz.getDeclaredFields()) &#123;</span><br><span class="line">            <span class="comment">// 获取修饰符（public、private 等）</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">modifier</span> <span class="operator">=</span> Modifier.toString(field.getModifiers());</span><br><span class="line">            <span class="comment">// 获取类型名</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">typeName</span> <span class="operator">=</span> field.getType().getSimpleName();</span><br><span class="line">            System.out.println(modifier + <span class="string">&quot; &quot;</span> + typeName + <span class="string">&quot; &quot;</span> + field.getName());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 输出：</span></span><br><span class="line">        <span class="comment">// public String name</span></span><br><span class="line">        <span class="comment">// protected int age</span></span><br><span class="line">        <span class="comment">// private String password</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 获取方法 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 方法信息 ===&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Method method : clazz.getDeclaredMethods()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">modifier</span> <span class="operator">=</span> Modifier.toString(method.getModifiers());</span><br><span class="line">            <span class="type">String</span> <span class="variable">returnType</span> <span class="operator">=</span> method.getReturnType().getSimpleName();</span><br><span class="line">            <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> method.getName();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 获取参数类型</span></span><br><span class="line">            Class&lt;?&gt;[] paramTypes = method.getParameterTypes();</span><br><span class="line">            <span class="type">String</span> <span class="variable">params</span> <span class="operator">=</span> Arrays.stream(paramTypes)</span><br><span class="line">                    .map(Class::getSimpleName)</span><br><span class="line">                    .collect(Collectors.joining(<span class="string">&quot;, &quot;</span>));</span><br><span class="line"></span><br><span class="line">            System.out.println(modifier + <span class="string">&quot; &quot;</span> + returnType + <span class="string">&quot; &quot;</span></span><br><span class="line">                    + methodName + <span class="string">&quot;(&quot;</span> + params + <span class="string">&quot;)&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 获取构造方法 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 构造方法 ===&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Constructor&lt;?&gt; constructor : clazz.getDeclaredConstructors()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">modifier</span> <span class="operator">=</span> Modifier.toString(constructor.getModifiers());</span><br><span class="line">            <span class="type">String</span> <span class="variable">constructorName</span> <span class="operator">=</span> constructor.getName();</span><br><span class="line"></span><br><span class="line">            Class&lt;?&gt;[] paramTypes = constructor.getParameterTypes();</span><br><span class="line">            <span class="type">String</span> <span class="variable">params</span> <span class="operator">=</span> Arrays.stream(paramTypes)</span><br><span class="line">                    .map(Class::getSimpleName)</span><br><span class="line">                    .collect(Collectors.joining(<span class="string">&quot;, &quot;</span>));</span><br><span class="line"></span><br><span class="line">            System.out.println(modifier + <span class="string">&quot; &quot;</span> + constructorName + <span class="string">&quot;(&quot;</span> + params + <span class="string">&quot;)&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="反射创建对象与调用方法"><a href="#反射创建对象与调用方法" class="headerlink" title="反射创建对象与调用方法"></a>反射创建对象与调用方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectInvoke</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        Class&lt;?&gt; clazz = Person.class;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 创建对象 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;=== 创建对象 ===&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式一：使用无参构造创建对象</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj1</span> <span class="operator">=</span> clazz.getDeclaredConstructor().newInstance();</span><br><span class="line">        System.out.println(<span class="string">&quot;无参构造创建：&quot;</span> + obj1);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 方式二：使用有参构造创建对象</span></span><br><span class="line">        Constructor&lt;?&gt; constructor = clazz.getConstructor(String.class, <span class="type">int</span>.class);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj2</span> <span class="operator">=</span> constructor.newInstance(<span class="string">&quot;张三&quot;</span>, <span class="number">25</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;有参构造创建：&quot;</span> + obj2);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 操作属性 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 操作属性 ===&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 操作公共属性</span></span><br><span class="line">        <span class="type">Field</span> <span class="variable">nameField</span> <span class="operator">=</span> clazz.getField(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        nameField.set(obj2, <span class="string">&quot;李四&quot;</span>);  <span class="comment">// 设置属性值</span></span><br><span class="line">        System.out.println(<span class="string">&quot;修改后的 name：&quot;</span> + nameField.get(obj2));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 操作私有属性</span></span><br><span class="line">        <span class="type">Field</span> <span class="variable">passwordField</span> <span class="operator">=</span> clazz.getDeclaredField(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">        passwordField.setAccessible(<span class="literal">true</span>);  <span class="comment">// 必须设置可访问，否则会抛异常</span></span><br><span class="line">        passwordField.set(obj2, <span class="string">&quot;newPassword&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;私有 password：&quot;</span> + passwordField.get(obj2));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 调用方法 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 调用方法 ===&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用公共方法</span></span><br><span class="line">        <span class="type">Method</span> <span class="variable">sayHello</span> <span class="operator">=</span> clazz.getMethod(<span class="string">&quot;sayHello&quot;</span>);</span><br><span class="line">        sayHello.invoke(obj2);  <span class="comment">// 输出：你好，我是李四</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用重载方法：需要指定参数类型</span></span><br><span class="line">        <span class="comment">// 假设 Person 有一个 sayHello(String greeting) 方法</span></span><br><span class="line">        <span class="comment">// Method greetMethod = clazz.getMethod(&quot;sayHello&quot;, String.class);</span></span><br><span class="line">        <span class="comment">// greetMethod.invoke(obj2, &quot;早上好&quot;);  // 输出：早上好，我是李四</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用私有方法</span></span><br><span class="line">        <span class="type">Method</span> <span class="variable">privateMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;privateMethod&quot;</span>);</span><br><span class="line">        privateMethod.setAccessible(<span class="literal">true</span>);  <span class="comment">// 设置可访问</span></span><br><span class="line">        privateMethod.invoke(obj2);  <span class="comment">// 输出：这是私有方法</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ============ 动态调用示例 ============</span></span><br><span class="line">        System.out.println(<span class="string">&quot;\n=== 动态调用 ===&quot;</span>);</span><br><span class="line">        <span class="comment">// 假设我们要根据配置调用不同的方法</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> <span class="string">&quot;sayHello&quot;</span>;</span><br><span class="line">        <span class="type">Method</span> <span class="variable">dynamicMethod</span> <span class="operator">=</span> clazz.getMethod(methodName);</span><br><span class="line">        dynamicMethod.invoke(obj2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="反射优缺点"><a href="#反射优缺点" class="headerlink" title="反射优缺点"></a>反射优缺点</h3><table><thead><tr><th>方面</th><th>说明</th></tr></thead><tbody><tr><td><strong>优点</strong></td><td>动态加载类、框架基石（Spring&#x2F;Hibernate）、运行时操作、极大灵活性</td></tr><tr><td><strong>缺点</strong></td><td>性能开销大（比直接调用慢数十倍）、破坏封装性、存在安全风险</td></tr><tr><td><strong>使用建议</strong></td><td>日常业务代码少用，框架和工具类中常用</td></tr></tbody></table><h3 id="反射的实际应用场景"><a href="#反射的实际应用场景" class="headerlink" title="反射的实际应用场景"></a>反射的实际应用场景</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 场景一：Spring 依赖注入</span></span><br><span class="line"><span class="comment">// Spring 容器在启动时通过反射创建 Bean 并注入属性</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景二：JSON 序列化（Fastjson、Gson）</span></span><br><span class="line"><span class="comment">// 通过反射读取对象的属性，将它们转换为 JSON</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景三：注解处理器</span></span><br><span class="line"><span class="comment">// 通过反射读取类、方法、属性上的注解，做相应处理</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景四：通用对象克隆</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; T <span class="title function_">deepClone</span><span class="params">(T obj)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">ByteArrayOutputStream</span> <span class="variable">bos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">    <span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(bos);</span><br><span class="line">    oos.writeObject(obj);</span><br><span class="line"></span><br><span class="line">    <span class="type">ByteArrayInputStream</span> <span class="variable">bis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(bos.toByteArray());</span><br><span class="line">    <span class="type">ObjectInputStream</span> <span class="variable">ois</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(bis);</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="type">T</span> <span class="variable">clone</span> <span class="operator">=</span> (T) ois.readObject();</span><br><span class="line">    <span class="keyword">return</span> clone;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="📌-总结"><a href="#📌-总结" class="headerlink" title="📌 总结"></a>📌 总结</h2><p>本文系统地总结了 Java 基础的核心知识点：</p><h3 id="知识框架图"><a href="#知识框架图" class="headerlink" title="知识框架图"></a>知识框架图</h3><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>mindmap  root((Java 基础))    Java 程序运行流程      JavaC 编译      ClassLoader 加载      JVM 执行      字节码跨平台    数据类型      基本类型 8 种      引用类型      类型转换    面向对象      封装      继承      多态    集合框架      List      Set      Map    多线程      线程创建      线程同步      生产者消费者    I/O 流      字节流      字符流      对象序列化    泛型      泛型类      泛型方法      通配符    反射      获取 Class      操作属性      调用方法</pre></div><h3 id="核心要点回顾"><a href="#核心要点回顾" class="headerlink" title="核心要点回顾"></a>核心要点回顾</h3><table><thead><tr><th>知识点</th><th>核心概念</th><th>关键点</th></tr></thead><tbody><tr><td><strong>数据类型</strong></td><td>基本类型 vs 引用类型</td><td>自动转型、强制转型、精度丢失</td></tr><tr><td><strong>面向对象</strong></td><td>封装、继承、多态</td><td>private&#x2F;protected&#x2F;public、extends、override&#x2F;overload</td></tr><tr><td><strong>集合框架</strong></td><td>List&#x2F;Set&#x2F;Map</td><td>ArrayList&#x2F;HashSet&#x2F;HashMap 最常用</td></tr><tr><td><strong>多线程</strong></td><td>生命周期、同步机制</td><td>synchronized、wait&#x2F;notify、线程安全</td></tr><tr><td><strong>I&#x2F;O 流</strong></td><td>字节流、字符流、对象流</td><td>try-with-resources、自动关闭</td></tr><tr><td><strong>泛型</strong></td><td>类型参数化、安全检查</td><td>&lt;? extends T&gt;、&lt;? super T&gt;</td></tr><tr><td><strong>反射</strong></td><td>运行时动态操作</td><td>Class.forName、Method.invoke</td></tr></tbody></table><blockquote><p>纸上得来终觉浅，绝知此事要躬行 🚀</p><p>学习 Java 最好的方式就是多敲代码，遇到问题多思考。阅读源码（如 JDK 源码、Spring 源码）是提升内功的有效途径。</p></blockquote><hr><p><em>📅 本文首次发布于 2026 年 5 月 20 日</em></p>]]>
    </content>
    <id>https://blog.codenav.top/java-basics/</id>
    <link href="https://blog.codenav.top/java-basics/"/>
    <published>2026-05-20T01:00:00.000Z</published>
    <summary>
      <![CDATA[<h1 id="Java-基础核心总结-📚"><a href="#Java-基础核心总结-📚" class="headerlink" title="Java 基础核心总结 📚"></a>Java 基础核心总结 📚</h1><blockquote>
<p>Java 是一门面]]>
    </summary>
    <title>Java 基础核心总结</title>
    <updated>2026-05-24T04:59:33.094Z</updated>
  </entry>
</feed>
